Modified Files: glib/ChangeLog glib/glib.def glib/glib/giochannel.c
authorRon Steinke <rsteinke@src.gnome.org>
Fri, 20 Jul 2001 20:14:37 +0000 (20:14 +0000)
committerRon Steinke <rsteinke@src.gnome.org>
Fri, 20 Jul 2001 20:14:37 +0000 (20:14 +0000)
     Modified Files:
  glib/ChangeLog glib/glib.def glib/glib/giochannel.c
  glib/glib/giochannel.h glib/glib/giounix.c
  glib/glib/giowin32.c
  glib/docs/reference/glib/glib-sections.txt
  glib/tests/iochannel-test.c glib/tests/unicode-collate.c
  glib/tests/unicode-normalize.c
     Added Files:
  glib/tests/iochannel-test-infile

        * glib/giochannel.c: API changes, fixes to
        error handling, some internal restructuring
        * glib/giochannel.h: API changes, documentation for
        elements in GIOChannel structure
        * glib/giounix.c: Matched API changes, implemented
        backend to set is_readable, is_writeable, is_seekable
        flags, added a test to catch large values of count
        for which the behavior of write() is undefined
        * glib/giowin32.c: Changed to match new prototypes for
        io_close() and io_seek(), removed references to
        G_IO_STATUS_INTR, set is_seekable flag in channel
        creation functions
        * glib.def: Renamed g_channel_error_quark() and
        g_channel_error_from_errno() to g_io_channel_error_quark() and
        g_io_channel_error_from_errno(); added new functions
        g_io_channel_get_buffered() and g_io_channel_set_buffered()
        * docs/reference/glib/glib-sections.txt: Modified iochannel
        section to reflect new functions and API changes
        * tests/iochannel-test.c: Fixed to work with API changes
        * tests/iochannel-test-infile: New file; input file
        for iochannel-test
        * tests/unicode-collate.c tests/unicode-normalize.c:
        Changed G_IO_FILE_MODE_READ to "r" to match API change

18 files changed:
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
docs/reference/glib/glib-sections.txt
glib.def
glib/giochannel.c
glib/giochannel.h
glib/giounix.c
glib/giowin32.c
tests/iochannel-test-infile [new file with mode: 0644]
tests/iochannel-test.c
tests/unicode-collate.c
tests/unicode-normalize.c

index 1129338..833af1e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 1129338..833af1e 100644 (file)
@@ -1,3 +1,29 @@
+Fri Jul 20 19:19:10 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: API changes, fixes to
+       error handling, some internal restructuring
+       * glib/giochannel.h: API changes, documentation for
+       elements in GIOChannel structure
+       * glib/giounix.c: Matched API changes, implemented
+       backend to set is_readable, is_writeable, is_seekable
+       flags, added a test to catch large values of count
+       for which the behavior of write() is undefined
+       * glib/giowin32.c: Changed to match new prototypes for
+       io_close() and io_seek(), removed references to
+       G_IO_STATUS_INTR, set is_seekable flag in channel
+       creation functions
+       * glib.def: Renamed g_channel_error_quark() and
+       g_channel_error_from_errno() to g_io_channel_error_quark() and
+       g_io_channel_error_from_errno(); added new functions
+       g_io_channel_get_buffered() and g_io_channel_set_buffered()
+       * docs/reference/glib/glib-sections.txt: Modified iochannel
+       section to reflect new functions and API changes
+       * tests/iochannel-test.c: Fixed to work with API changes
+       * tests/iochannel-test-infile: New file; input file
+       for iochannel-test
+       * tests/unicode-collate.c tests/unicode-normalize.c:
+       Changed G_IO_FILE_MODE_READ to "r" to match API change
+
 Fri Jul 20 19:16:24 (UTC) 2001  Ron Steinke  <rsteinke@w-link.net>
 
        * gobject/glib-genmarshal.c: modified to create header files
index 80a0662..44608d9 100644 (file)
@@ -594,7 +594,6 @@ g_io_channel_init
 
 <SUBSECTION>
 g_io_channel_new_file
-GIOFileMode
 g_io_channel_read_chars
 g_io_channel_read_line
 g_io_channel_read_line_string
@@ -603,14 +602,14 @@ g_io_channel_write_chars
 g_io_channel_flush
 g_io_channel_seek_position
 GSeekType
-g_io_channel_close
+g_io_channel_shutdown
 
 <SUBSECTION>
-GChannelStatus
-GChannelError
-G_CHANNEL_ERROR
-g_channel_error_quark
-g_channel_error_from_errno
+GIOStatus
+GIOChannelError
+G_IO_CHANNEL_ERROR
+g_io_channel_error_quark
+g_io_channel_error_from_errno
 
 <SUBSECTION>
 g_io_channel_ref
@@ -638,10 +637,10 @@ g_io_channel_set_line_term
 G_IO_CHANNEL_UNIX_LINE_TERM
 G_IO_CHANNEL_DOS_LINE_TERM
 G_IO_CHANNEL_MACINTOSH_LINE_TERM
-G_IO_CHANNEL_DEFAULT_LINE_TERM
+g_io_channel_get_buffered
+g_io_channel_set_buffered
 g_io_channel_get_encoding
 g_io_channel_set_encoding
-G_IO_CHANNEL_ENCODE_RAW
 
 <SUBSECTION Private>
 g_io_channel_win32_new_fd
@@ -656,6 +655,7 @@ g_io_channel_read
 GIOError
 g_io_channel_write
 g_io_channel_seek
+g_io_channel_close
 </SECTION>
 
 <SECTION>
index 51448f3..e297349 100644 (file)
--- a/glib.def
+++ b/glib.def
@@ -51,8 +51,6 @@ EXPORTS
        g_cache_new
        g_cache_remove
        g_cache_value_foreach
-       g_channel_error_quark
-       g_channel_error_from_errno
        g_clear_error
        g_completion_add_items
        g_completion_clear_items
@@ -186,9 +184,12 @@ EXPORTS
        g_io_add_watch
        g_io_add_watch_full
        g_io_channel_close
+       g_io_channel_error_from_errno
+       g_io_channel_error_quark
        g_io_channel_flush
        g_io_channel_get_buffer_condition
        g_io_channel_get_buffer_size
+       g_io_channel_get_buffered
        g_io_channel_get_encoding
        g_io_channel_get_flags
        g_io_channel_get_line_term
@@ -203,6 +204,7 @@ EXPORTS
        g_io_channel_seek
        g_io_channel_seek_position
        g_io_channel_set_buffer_size
+       g_io_channel_set_buffered
        g_io_channel_set_encoding
        g_io_channel_set_flags
        g_io_channel_set_line_term
index 1dfd1d6..1560920 100644 (file)
 
 #define G_IO_NICE_BUF_SIZE     1024
 
-static GIOError                g_io_error_get_from_g_error     (GIOStatus status,
+static GIOError                g_io_error_get_from_g_error     (GIOStatus    status,
                                                         GError      *err);
 static void            g_io_channel_purge              (GIOChannel  *channel);
 static GIOStatus       g_io_channel_fill_buffer        (GIOChannel  *channel,
                                                         GError     **err);
+static GIOStatus       g_io_channel_read_line_backend  (GIOChannel  *channel,
+                                                        gsize       *length,
+                                                        gsize       *terminator_pos,
+                                                        GError     **error);
 
 void
 g_io_channel_init (GIOChannel *channel)
 {
-  channel->channel_flags = 0;
   channel->ref_count = 1;
-  channel->encoding = NULL;
+  channel->encoding = g_strdup ("UTF-8");
   channel->line_term = NULL;
   channel->buf_size = G_IO_NICE_BUF_SIZE;
-  channel->read_buf = g_string_sized_new (G_IO_NICE_BUF_SIZE);
-  channel->encoded_read_buf = NULL; /* Allocate if we get an encoding */
-  channel->write_buf = g_string_sized_new (G_IO_NICE_BUF_SIZE);
+  channel->read_cd = (GIConv) -1;
+  channel->write_cd = (GIConv) -1;
+  channel->read_buf = NULL; /* Lazy allocate buffers */
+  channel->encoded_read_buf = NULL;
+  channel->write_buf = NULL;
+  channel->partial_write_buf[0] = '\0';
   channel->use_buffer = TRUE;
   channel->do_encode = FALSE;
-  channel->ready_to_read = TRUE;
-  channel->ready_to_write = TRUE;
   channel->close_on_unref = FALSE;
-  channel->seekable_cached = FALSE;
 }
 
 void 
@@ -93,15 +96,17 @@ g_io_channel_unref (GIOChannel *channel)
       else
         g_io_channel_purge (channel);
       g_free (channel->encoding);
-      if (channel->do_encode)
-        {
-          g_iconv_close (channel->read_cd);
-          g_iconv_close (channel->write_cd);
-          g_string_free (channel->encoded_read_buf, TRUE);
-        }
+      if (channel->read_cd != (GIConv) -1)
+        g_iconv_close (channel->read_cd);
+      if (channel->write_cd != (GIConv) -1)
+        g_iconv_close (channel->write_cd);
       g_free (channel->line_term);
-      g_string_free (channel->read_buf, TRUE);
-      g_string_free (channel->write_buf, TRUE);
+      if (channel->read_buf)
+        g_string_free (channel->read_buf, TRUE);
+      if (channel->write_buf)
+        g_string_free (channel->write_buf, TRUE);
+      if (channel->encoded_read_buf)
+        g_string_free (channel->encoded_read_buf, TRUE);
       channel->funcs->io_free (channel);
     }
 }
@@ -227,6 +232,7 @@ g_io_channel_seek  (GIOChannel   *channel,
   GIOStatus status;
 
   g_return_val_if_fail (channel != NULL, G_IO_ERROR_UNKNOWN);
+  g_return_val_if_fail (channel->is_seekable, G_IO_ERROR_UNKNOWN);
 
   switch (type)
     {
@@ -256,8 +262,9 @@ g_io_channel_seek  (GIOChannel   *channel,
 /**
  * g_io_channel_new_file:
  * @filename: A string containing the name of a file.
- * @mode: A #GIOFileMode
- * @error: A location to return an error of type %G_IO_CHANNEL_ERROR.
+ * @mode: One of "r", "w", "a", "r+", "w+", "a+". These have
+ *        the same meaning as in fopen().
+ * @error: A location to return an error of type %G_IO_FILE_ERROR.
  *
  * Open a file @filename as a #GIOChannel using mode @mode. This
  * channel will be closed when the last reference to it is dropped,
@@ -296,8 +303,9 @@ g_io_channel_close (GIOChannel *channel)
     }
   
   channel->close_on_unref = FALSE; /* Because we already did */
-  channel->ready_to_read = FALSE;
-  channel->ready_to_write = FALSE;
+  channel->is_readable = FALSE;
+  channel->is_writeable = FALSE;
+  channel->is_seekable = FALSE;
 }
 
 /**
@@ -309,40 +317,60 @@ g_io_channel_close (GIOChannel *channel)
  * Close an IO channel. Any pending data to be written will be
  * flushed. The channel will not be freed until the
  * last reference is dropped using g_io_channel_unref().
+ *
+ * Return value:
  **/
 GIOStatus
 g_io_channel_shutdown (GIOChannel *channel,
                       gboolean    flush,
-                      GError     **err)
+                      GError    **err)
 {
-  GIOStatus status;
-  GIOStatus result = G_IO_STATUS_NORMAL;
+  GIOStatus status, result;
+  GError *tmperr = NULL;
   
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+  g_return_val_if_fail (err == NULL || *err == NULL, G_IO_STATUS_ERROR);
 
-  if (flush && channel->write_buf->len > 0)
+  if (flush && channel->write_buf && channel->write_buf->len > 0)
     {
       GIOFlags flags;
       
       /* Set the channel to blocking, to avoid a busy loop
        */
       flags = g_io_channel_get_flags (channel);
-      status = g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
-      if (status != G_IO_STATUS_NORMAL)
-       result = G_IO_STATUS_ERROR; 
-      
-      g_io_channel_flush (channel, err);
+      /* Ignore any errors here, they're irrelevant */
+      g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
+
+      result = g_io_channel_flush (channel, &tmperr);
+
+      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';
+        }
     }
+  else
+    result = G_IO_STATUS_NORMAL;
 
-  status = channel->funcs->io_close (channel, err && *err ? NULL : err);
-  if (status != G_IO_STATUS_NORMAL)
-    result = G_IO_STATUS_ERROR; 
+  status = channel->funcs->io_close (channel, err);
 
   channel->close_on_unref = FALSE; /* Because we already did */
-  channel->ready_to_read = FALSE;
-  channel->ready_to_write = FALSE;
+  channel->is_readable = FALSE;
+  channel->is_writeable = FALSE;
+  channel->is_seekable = FALSE;
 
-  return result;
+  if (status != G_IO_STATUS_NORMAL)
+    {
+      g_clear_error (&tmperr);
+      return status;
+    }
+  else if (result != G_IO_STATUS_NORMAL)
+    {
+      g_propagate_error (err, tmperr);
+      return result;
+    }
+  else
+    return G_IO_STATUS_NORMAL;
 }
 
 /* This function is used for the final flush on close or unref */
@@ -350,10 +378,11 @@ static void
 g_io_channel_purge (GIOChannel *channel)
 {
   GError *err = NULL;
+  GIOStatus status;
 
   g_return_if_fail (channel != NULL);
 
-  if (channel->write_buf->len > 0)
+  if (channel->write_buf && channel->write_buf->len > 0)
     {
       GIOFlags flags;
       
@@ -362,7 +391,7 @@ g_io_channel_purge (GIOChannel *channel)
       flags = g_io_channel_get_flags (channel);
       g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
 
-      g_io_channel_flush (channel, &err);
+      status = g_io_channel_flush (channel, &err);
 
       if (err)
        { /* No way to return the error */
@@ -373,10 +402,21 @@ g_io_channel_purge (GIOChannel *channel)
 
   /* Flush these in case anyone tries to close without unrefing */
 
-  g_string_truncate (channel->read_buf, 0);
+  if (channel->read_buf)
+    g_string_truncate (channel->read_buf, 0);
+  if (channel->write_buf)
+    g_string_truncate (channel->write_buf, 0);
   if (channel->do_encode)
-    g_string_truncate (channel->encoded_read_buf, 0);
-  g_string_truncate (channel->write_buf, 0);
+    {
+      if (channel->encoded_read_buf)
+        g_string_truncate (channel->encoded_read_buf, 0);
+
+      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';
+        }
+    }
 }
 
 GSource *
@@ -435,19 +475,26 @@ g_io_add_watch (GIOChannel    *channel,
 GIOCondition
 g_io_channel_get_buffer_condition (GIOChannel *channel)
 {
-  return
-    ((channel->read_buf && channel->read_buf->len > 0) ? G_IO_IN : 0) |
-    ((channel->write_buf && channel->write_buf->len <= channel->buf_size) ? G_IO_OUT : 0);
+  GIOCondition condition = 0;
+
+  if ((channel->read_buf && (channel->read_buf->len > 0)) /* FIXME full chars how? */
+    || (channel->encoded_read_buf && (channel->encoded_read_buf->len > 0)))
+    condition &= G_IO_IN;
+
+  if (channel->write_buf && (channel->write_buf->len < channel->buf_size))
+    condition &= G_IO_OUT;
+
+  return condition;
 }
 
 /**
- * g_channel_error_from_errno:
+ * g_io_channel_error_from_errno:
  * @en: An errno error number, e.g. EINVAL
  *
  * Return value: A #GIOChannelError error number, e.g. %G_IO_CHANNEL_ERROR_INVAL
  **/
 GIOChannelError
-g_channel_error_from_errno (gint en)
+g_io_channel_error_from_errno (gint en)
 {
 #ifdef EAGAIN
   g_return_val_if_fail (en != EAGAIN, G_IO_CHANNEL_ERROR_FAILED);
@@ -458,19 +505,21 @@ g_channel_error_from_errno (gint en)
 
   switch (en)
     {
-#ifdef EACCES
-    case EACCES:
-      return G_IO_CHANNEL_ERROR_ACCES;
-#endif
-
 #ifdef EBADF
     case EBADF:
-      return G_IO_CHANNEL_ERROR_BADF;
+      g_warning("Invalid file descriptor.\n");
+      return G_IO_CHANNEL_ERROR_FAILED;
 #endif
 
 #ifdef EFAULT
     case EFAULT:
-      return G_IO_CHANNEL_ERROR_FAULT;
+      g_warning("File descriptor outside valid address space.\n");
+      return G_IO_CHANNEL_ERROR_FAILED;
+#endif
+
+#ifdef EFBIG
+    case EFBIG:
+      return G_IO_CHANNEL_ERROR_FBIG;
 #endif
 
 #ifdef EINVAL
@@ -488,36 +537,26 @@ g_channel_error_from_errno (gint en)
       return G_IO_CHANNEL_ERROR_ISDIR;
 #endif
 
-#ifdef EMFILE
-    case EMFILE:
-      return G_IO_CHANNEL_ERROR_MFILE;
-#endif
-
-#ifdef ENOLCK
-    case ENOLCK:
-      return G_IO_CHANNEL_ERROR_NOLCK;
-#endif
-
 #ifdef ENOSPC
     case ENOSPC:
       return G_IO_CHANNEL_ERROR_NOSPC;
 #endif
 
-#ifdef EPERM
-    case EPERM:
-      return G_IO_CHANNEL_ERROR_PERM;
+#ifdef ENXIO
+    case ENXIO:
+      return G_IO_CHANNEL_ERROR_NXIO;
 #endif
-      
+
+#ifdef EOVERFLOW
+    case EOVERFLOW:
+      return G_IO_CHANNEL_ERROR_OVERFLOW;
+#endif
+
 #ifdef EPIPE
     case EPIPE:
       return G_IO_CHANNEL_ERROR_PIPE;
 #endif
 
-#ifdef ESPIPE
-    case ESPIPE:
-      return G_IO_CHANNEL_ERROR_SPIPE;
-#endif
-
     default:
       return G_IO_CHANNEL_ERROR_FAILED;
     }
@@ -539,6 +578,9 @@ 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;
+
   channel->buf_size = size;
 }
 
@@ -572,6 +614,8 @@ g_io_channel_set_line_term (GIOChannel      *channel,
 {
   g_return_if_fail (channel != NULL);
   g_return_if_fail (!line_term || line_term[0]); /* Disallow "" */
+  g_return_if_fail (!line_term || g_utf8_validate (line_term, -1, NULL));
+                   /* Require valid UTF-8 */
 
   g_free (channel->line_term);
   channel->line_term = g_strdup (line_term);
@@ -621,17 +665,31 @@ g_io_channel_set_flags (GIOChannel *channel,
 /**
  * g_io_channel_get_flags:
  * @channel: a #GIOChannel
- * @flags: the flags which are set on the channel
  *
  * Gets the current flags for a #GIOChannel, including read-only
  * flags such as %G_IO_FLAG_IS_READABLE.
+ *
+ * Return value: the flags which are set on the channel
  **/
 GIOFlags
 g_io_channel_get_flags (GIOChannel *channel)
 {
+  GIOFlags flags;
+
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
 
-  return (* channel->funcs->io_get_flags) (channel);
+  flags = (* channel->funcs->io_get_flags) (channel);
+
+  /* Cross implementation code */
+
+  if (channel->is_seekable)
+    flags |= G_IO_FLAG_IS_SEEKABLE;
+  if (channel->is_readable)
+    flags |= G_IO_FLAG_IS_READABLE;
+  if (channel->is_writeable)
+    flags |= G_IO_FLAG_IS_WRITEABLE;
+
+  return flags;
 }
 
 /**
@@ -662,16 +720,23 @@ g_io_channel_seek_position        (GIOChannel* channel,
   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 (channel->is_seekable, G_IO_STATUS_ERROR);
 
   switch (type)
     {
       case G_SEEK_CUR: /* The user is seeking relative to the head of the buffer */
-        if (channel->do_encode)
+        if (channel->use_buffer)
           {
-            g_warning ("Seek type G_SEEK_CUR not allowed for this channel's encoding.\n");
-            return G_IO_STATUS_ERROR;
+            if (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)
+              {
+                g_warning ("Seek type G_SEEK_CUR not allowed for this"
+                  " channel's encoding.\n");
+                return G_IO_STATUS_ERROR;
+              }
+          if (channel->read_buf)
+            offset -= channel->read_buf->len;
           }
-          break;
+        break;
       case G_SEEK_SET:
       case G_SEEK_END:
         break;
@@ -680,44 +745,30 @@ g_io_channel_seek_position        (GIOChannel* channel,
         return G_IO_STATUS_ERROR;
     }
 
-  /* Do a quick check for easy failure
-   */
-  if (!channel->seekable_cached)
-    g_io_channel_get_flags (channel);
-
-  g_assert (channel->seekable_cached);
-
-  if (!channel->is_seekable)
-    {
-      g_set_error (error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_SPIPE,
-        _("Seeking not allowed on this type of channel"));
-      return G_IO_STATUS_ERROR;
-    }
-
   if (channel->use_buffer)
     {
       status = g_io_channel_flush (channel, error);
       if (status != G_IO_STATUS_NORMAL)
         return status;
-
-      if (type == G_SEEK_CUR)
-        {
-          g_assert (!channel->do_encode);
-          offset -= channel->read_buf->len;
-        }
     }
 
   status = channel->funcs->io_seek (channel, offset, type, error);
 
   if ((status == G_IO_STATUS_NORMAL) && (channel->use_buffer))
     {
-      g_string_erase (channel->read_buf, 0, channel->read_buf->len);
-      g_string_erase (channel->encoded_read_buf, 0, channel->read_buf->len);
-      channel->ready_to_write = TRUE;
+      if (channel->read_buf)
+        g_string_truncate (channel->read_buf, 0);
       if (channel->do_encode) /* Conversion state no longer matches position in file */
         {
           g_iconv (channel->read_cd, NULL, NULL, NULL, NULL);
           g_iconv (channel->write_cd, NULL, NULL, NULL, NULL);
+          if (channel->encoded_read_buf)
+            g_string_truncate (channel->encoded_read_buf, 0);
+        }
+      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';
         }
     }
 
@@ -740,19 +791,18 @@ g_io_channel_flush (GIOChannel    *channel,
                    GError     **error)
 {
   GIOStatus status;
-  gsize this_time, bytes_written = 0;
+  gsize this_time = 1, bytes_written = 0;
 
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail ((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR);
 
-  if (channel->write_buf->len == 0)
-    {
-      channel->ready_to_read = TRUE;
-      return G_IO_STATUS_NORMAL;
-    }
+  if (channel->write_buf && channel->write_buf->len > 0)
+    return G_IO_STATUS_NORMAL;
 
   do
     {
+      g_assert (this_time > 0);
+
       status = channel->funcs->io_write (channel,
                                       channel->write_buf->str + bytes_written,
                                       channel->write_buf->len - bytes_written,
@@ -760,20 +810,54 @@ g_io_channel_flush (GIOChannel    *channel,
       bytes_written += this_time;
     }
   while ((bytes_written < channel->write_buf->len)
-         && (status == G_IO_STATUS_NORMAL)
-         && (this_time > 0)); /* Prevent an infinite loop */
+         && (status == G_IO_STATUS_NORMAL));
 
   g_string_erase (channel->write_buf, 0, bytes_written);
 
-  if (status != G_IO_STATUS_NORMAL)
-    return status;
+  return status;
+}
 
-  if (channel->write_buf->len > 0)
-    return G_IO_STATUS_AGAIN;
+/**
+ * g_io_channel_set_buffered:
+ * @channel: a #GIOChannel
+ * @buffered: whether to set the channel buffered or unbuffered
+ *
+ * The buffering state can only be set if the channel's encoding
+ * is %NULL. For any other encoding, the channel must be buffered.
+ *
+ * The default state of the channel is buffered.
+ **/
+void
+g_io_channel_set_buffered      (GIOChannel *channel,
+                                gboolean    buffered)
+{
+  g_return_if_fail (channel != NULL);
 
-  channel->ready_to_read = TRUE;
+  if (channel->encoding != NULL)
+    {
+      g_warning ("Need to have NULL encoding to set the buffering state of the "
+                 "channel.\n");
+      return;
+    }
 
-  return G_IO_STATUS_NORMAL;
+  g_return_if_fail (!channel->read_buf || channel->read_buf->len == 0);
+  g_return_if_fail (!channel->write_buf || channel->write_buf->len == 0);
+
+  channel->use_buffer = buffered;
+}
+
+/**
+ * g_io_channel_get_buffered:
+ * @channel: a #GIOChannel
+ *
+ * Return Value: the buffering state of the channel
+ **/
+gboolean
+g_io_channel_get_buffered      (GIOChannel *channel)
+{
+  g_return_val_if_fail (channel != NULL, FALSE);
+
+  return channel->use_buffer;
 }
 
 /**
@@ -783,17 +867,13 @@ g_io_channel_flush (GIOChannel    *channel,
  * @error: location to store an error of type #GConvertError.
  *
  * Set the encoding for the input/output of the channel. The internal
- * encoding is always UTF-8.
- *
- * The encoding %G_IO_CHANNEL_ENCODE_RAW disables encoding and turns
- * off internal buffering. Data can be read from it only with
- * g_io_channel_read_chars(). Both %G_IO_CHANNEL_ENCODE_RAW and
- * the default state (no encoding, but buffered)
- * are safe to use with binary data.
+ * encoding is always UTF-8. The default encoding for the
+ * external file is UTF-8.
  *
- * Encodings
- * other than the default and %G_IO_CHANNEL_ENCODE_RAW cannot
-b * use g_io_channel_seek_position() with seek type %G_SEEK_CUR,
+ * The encoding %NULL is safe to use with binary data.
+ * Encodings other than %NULL must use a buffered channel.
+ * Encodings other than %NULL and UTF-8 cannot
+ * use g_io_channel_seek_position() with seek type %G_SEEK_CUR,
  * and cannot mix reading and writing if the channel is
  * a file without first doing a seek of type %G_SEEK_SET or
  * %G_SEEK_END.
@@ -807,7 +887,7 @@ b * use g_io_channel_seek_position() with seek type %G_SEEK_CUR,
  *    repositioned by a call to g_io_channel_seek_position().
  *    (This flushes all the internal buffers.)
  *
- * 3. The current encoding is %G_IO_CHANNEL_ENCODE_RAW.
+ * 3. The current encoding is %NULL or UTF-8.
  *
  * Return Value: %G_IO_STATUS_NORMAL if the encoding was succesfully set.
  **/
@@ -822,61 +902,65 @@ g_io_channel_set_encoding (GIOChannel     *channel,
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail ((error == NULL) || (*error == NULL), G_IO_STATUS_ERROR);
 
-  /* Make sure the buffers are empty */
+  /* Make sure the encoded buffers are empty */
 
-  g_return_val_if_fail (channel->read_buf->len == 0, G_IO_STATUS_ERROR);
-  g_return_val_if_fail (channel->write_buf->len == 0, G_IO_STATUS_ERROR);
-  g_return_val_if_fail (channel->encoded_read_buf == NULL ||
+  g_return_val_if_fail (!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);
 
-  did_encode = channel->do_encode;
-
-  if (!encoding || strcmp (encoding, "UTF8") == 0 || strcmp (encoding, "UTF-8") == 0)
+  if (!channel->use_buffer)
     {
+      g_warning ("Need to set the channel buffered before setting the encoding.\n");
+      g_warning ("Assuming this is what you meant and acting accordingly.\n");
+
       channel->use_buffer = TRUE;
-      channel->do_encode = FALSE;
-      read_cd = write_cd = (GIConv) -1; /* keep the compiler happy */
     }
-  else if (!strcmp (encoding, G_IO_CHANNEL_ENCODE_RAW))
+
+  did_encode = channel->do_encode;
+
+  if (!encoding || strcmp (encoding, "UTF8") == 0 || strcmp (encoding, "UTF-8") == 0)
     {
-      channel->use_buffer = FALSE;
       channel->do_encode = FALSE;
-      read_cd = write_cd = (GIConv) -1; /* keep the compiler happy */
+      read_cd = write_cd = (GIConv) -1;
     }
   else
     {
-      gint err;
-      const gchar *from_enc, *to_enc;
-
-      read_cd = g_iconv_open ("UTF-8", encoding);
+      gint err = 0;
+      const gchar *from_enc = NULL, *to_enc = NULL;
 
-      if (read_cd == (GIConv) -1)
+      if (channel->is_readable)
         {
-          err = errno;
-          from_enc = "UTF-8";
-          to_enc = encoding;
-          write_cd = (GIConv) -1; /* keep the compiler happy */
+          read_cd = g_iconv_open ("UTF-8", encoding);
+
+          if (read_cd == (GIConv) -1)
+            {
+              err = errno;
+              from_enc = "UTF-8";
+              to_enc = encoding;
+            }
         }
       else
+        read_cd = (GIConv) -1;
+
+      if (channel->is_writeable && err == 0)
         {
           write_cd = g_iconv_open (encoding, "UTF-8");
+
           if (write_cd == (GIConv) -1)
             {
               err = errno;
-              g_iconv_close (read_cd);
               from_enc = encoding;
               to_enc = "UTF-8";
             }
-          else
-            {
-              err = 0;
-              from_enc = to_enc = NULL; /* Keep the compiler happy */
-            }
         }
+      else
+        write_cd = (GIConv) -1;
 
-      if (err)
+      if (err != 0)
         {
-          /* Something went wrong.  */
+          g_assert (from_enc);
+          g_assert (to_enc);
+
           if (err == EINVAL)
             g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_NO_CONVERSION,
                          _("Conversion from character set `%s' to `%s' is not supported"),
@@ -885,33 +969,27 @@ g_io_channel_set_encoding (GIOChannel     *channel,
             g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
                          _("Could not open converter from `%s' to `%s': %s"),
                          from_enc, to_enc, strerror (errno));
+
+          if (read_cd != (GIConv) -1)
+            g_iconv_close (read_cd);
+          if (write_cd != (GIConv) -1)
+            g_iconv_close (write_cd);
+
           return G_IO_STATUS_ERROR;
         }
 
-      channel->use_buffer = TRUE;
       channel->do_encode = TRUE;
-      if (!did_encode)
-        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
     }
 
   /* The encoding is ok, so set the fields in channel */
 
-  if (did_encode)
-    {
-      g_iconv_close (channel->read_cd);
-      g_iconv_close (channel->write_cd);
-      if (!channel->do_encode)
-        {
-          g_string_free (channel->encoded_read_buf, TRUE);
-          channel->encoded_read_buf = NULL;
-        }
-    }
+  if (channel->read_cd != (GIConv) -1)
+    g_iconv_close (channel->read_cd);
+  if (channel->write_cd != (GIConv) -1)
+    g_iconv_close (channel->write_cd);
 
-  if (channel->do_encode)
-    {
-      channel->read_cd = read_cd;
-      channel->write_cd = write_cd;
-    }
+  channel->read_cd = read_cd;
+  channel->write_cd = write_cd;
 
   g_free (channel->encoding);
   channel->encoding = g_strdup (encoding);
@@ -947,21 +1025,20 @@ g_io_channel_fill_buffer (GIOChannel *channel,
   gsize read_size, cur_len, oldlen;
   GIOStatus status;
 
-  if (!channel->ready_to_read)
+  if (channel->is_seekable && channel->write_buf && channel->write_buf->len > 0)
     {
-    status = g_io_channel_flush (channel, err);
-    if (status != G_IO_STATUS_NORMAL)
-      return status;
+      status = g_io_channel_flush (channel, err);
+      if (status != G_IO_STATUS_NORMAL)
+        return status;
+    }
+  if (channel->is_seekable && channel->partial_write_buf[0] != '\0')
+    {
+      g_warning ("Partial character at end of write buffer not flushed.\n");
+      channel->partial_write_buf[0] = '\0';
     }
-  g_assert (channel->ready_to_read);
-
-  if (!channel->seekable_cached)
-    g_io_channel_get_flags (channel);
-
-  g_assert (channel->seekable_cached);
 
-  if (channel->is_seekable)
-    channel->ready_to_write = FALSE;
+  if (!channel->read_buf)
+    channel->read_buf = g_string_sized_new (channel->buf_size);
 
   cur_len = channel->read_buf->len;
 
@@ -980,22 +1057,32 @@ g_io_channel_fill_buffer (GIOChannel *channel,
 
   g_assert (channel->read_buf->len > 0);
 
+  if (channel->encoded_read_buf)
+    oldlen = channel->encoded_read_buf->len;
+  else
+    oldlen = 0;
+
   if (channel->do_encode)
     {
       size_t errnum, inbytes_left, outbytes_left;
       gchar *inbuf, *outbuf;
 
-      oldlen = channel->encoded_read_buf->len;
+      if (!channel->encoded_read_buf)
+        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
 
 reencode:
 
       inbytes_left = channel->read_buf->len;
-      outbytes_left = channel->buf_size;
+      outbytes_left = MIN (channel->buf_size / 4,
+                           channel->encoded_read_buf->allocated_len
+                           - channel->encoded_read_buf->len);
 
-      g_string_set_size (channel->encoded_read_buf, channel->encoded_read_buf->len + outbytes_left);
+      g_string_set_size (channel->encoded_read_buf,
+                         channel->encoded_read_buf->len + outbytes_left);
 
       inbuf = channel->read_buf->str;
-      outbuf = channel->encoded_read_buf->str + channel->encoded_read_buf->len - outbytes_left;
+      outbuf = channel->encoded_read_buf->str + channel->encoded_read_buf->len
+               - outbytes_left;
 
       errnum = g_iconv (channel->read_cd, &inbuf, &inbytes_left,
                        &outbuf, &outbytes_left);
@@ -1012,7 +1099,7 @@ reencode:
               case EINVAL:
                 if ((oldlen == channel->encoded_read_buf->len)
                   && (status == G_IO_STATUS_EOF))
-                  status = G_IO_STATUS_PARTIAL_CHARS;
+                  status = G_IO_STATUS_EOF;
                 else
                   status = G_IO_STATUS_NORMAL;
                 break;
@@ -1035,10 +1122,56 @@ reencode:
                 return G_IO_STATUS_ERROR;
             }
         }
-      if (channel->encoded_read_buf->len == 0)
+      g_assert ((status != G_IO_STATUS_NORMAL)
+               || (channel->encoded_read_buf->len > 0));
+    }
+  else if (channel->encoding) /* UTF-8 */
+    {
+      gchar *nextchar, *lastchar;
+
+      nextchar = channel->read_buf->str;
+      lastchar = channel->read_buf->str + channel->read_buf->len;
+
+      while (nextchar < lastchar)
         {
-          g_assert (status != G_IO_STATUS_NORMAL);
-          return status;
+          gunichar val_char;
+
+          val_char = g_utf8_get_char_validated (nextchar, lastchar - nextchar);
+
+          switch (val_char)
+            {
+              case -2:
+                /* stop, leave partial character in buffer */
+                lastchar = nextchar;
+                break;
+              case -1:
+                if (oldlen > channel->encoded_read_buf->len)
+                  status = G_IO_STATUS_NORMAL;
+                else
+                  {
+                    g_set_error (err, G_CONVERT_ERROR,
+                      G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+                      _("Invalid byte sequence in conversion input"));
+                    status = G_IO_STATUS_ERROR;
+                  }
+                lastchar = nextchar;
+                break;
+              default:
+                nextchar = g_utf8_next_char (nextchar);
+                break;
+            }
+        }
+
+      if (lastchar > channel->read_buf->str)
+        {
+          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);
         }
     }
 
@@ -1074,24 +1207,31 @@ g_io_channel_read_line (GIOChannel *channel,
                        GError    **error)
 {
   GIOStatus status;
-  GString *string;
   
   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 ((error == NULL) || (*error == NULL),
                        G_IO_STATUS_ERROR);
+  g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
 
-  string = g_string_sized_new (channel->buf_size);
+  status = g_io_channel_read_line_backend (channel, length, terminator_pos, error);
 
-  status = g_io_channel_read_line_string (channel, string, terminator_pos, error);
+  if (status == G_IO_STATUS_NORMAL)
+    {
+      GString *use_buf;
 
-  if (length)
-    *length = string->len;
+      if (channel->do_encode)
+        use_buf = channel->encoded_read_buf;
+      else
+        use_buf = channel->read_buf;
 
-  if (str_return)
-    *str_return = g_string_free (string, FALSE);
+      g_assert (use_buf);
+
+      *str_return = g_strndup (use_buf->str, *length);
+      g_string_erase (use_buf, 0, *length);
+    }
   else
-    g_string_free (string, TRUE);
+    *str_return = NULL;
   
   return status;
 }
@@ -1116,16 +1256,50 @@ g_io_channel_read_line_string (GIOChannel *channel,
                               gsize      *terminator_pos,
                                GError   **error)
 {
+  gsize length;
   GIOStatus status;
-  gsize checked_to, line_term_len, line_length;
-  GString *use_buf;
-  gboolean first_time = TRUE;
 
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail (buffer != 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);
+
+  if (buffer->len > 0)
+    g_string_truncate (buffer, 0); /* clear out the buffer */
+
+  status = g_io_channel_read_line_backend (channel, &length, terminator_pos, error);
+
+  if (status == G_IO_STATUS_NORMAL)
+    {
+      GString *use_buf;
+
+      if (channel->do_encode)
+        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);
+    }
+
+  return status;
+}
+
+
+static GIOStatus
+g_io_channel_read_line_backend (GIOChannel *channel,
+                                gsize      *length,
+                                gsize      *terminator_pos,
+                                GError    **error)
+{
+  GIOStatus status;
+  gsize checked_to, line_term_len, line_length, got_term_len;
+  GString *use_buf;
+  gboolean first_time = TRUE;
+
   if (!channel->use_buffer)
     {
       /* Can't do a raw read in read_line */
@@ -1134,44 +1308,70 @@ g_io_channel_read_line_string (GIOChannel *channel,
       return G_IO_STATUS_ERROR;
     }
 
-  if (!channel->ready_to_read)
+  if (channel->encoding)
     {
-      status = g_io_channel_flush (channel, error);
-      if (status != G_IO_STATUS_NORMAL)
-        return status;
+      if (!channel->encoded_read_buf)
+        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
+      use_buf = channel->encoded_read_buf;
     }
-  g_assert (channel->ready_to_read);
-
-  if (channel->do_encode)
-    use_buf = channel->encoded_read_buf;
   else
-    use_buf = channel->read_buf;
+    {
+      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)
     line_term_len = strlen (channel->line_term);
   else
-    line_term_len = 2;
+    line_term_len = 3;
+    /* This value used for setting checked_to, it's the longest of the four
+     * we autodetect for.
+     */
 
   checked_to = 0;
 
   while (TRUE)
     {
+      gchar *nextchar, *lastchar;
+
       if (!first_time || (use_buf->len == 0))
         {
+read_again:
           status = g_io_channel_fill_buffer (channel, error);
           switch (status)
             {
               case G_IO_STATUS_NORMAL:
-                g_assert (use_buf->len > 0);
+                if (use_buf->len == 0)
+                  /* Can happen when using conversion and only read
+                   * part of a character
+                   */
+                  {
+                    first_time = FALSE;
+                    continue;
+                  }
                 break;
               case G_IO_STATUS_EOF:
-              case G_IO_STATUS_PARTIAL_CHARS:
                 if (use_buf->len == 0)
-                  return status;
+                  {
+                    *length = 0;
+
+                    if (channel->do_encode && channel->read_buf->len != 0)
+                      /* using GIConv conversion, unconverted chars */
+                      {
+                        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;
               default:
+                *length = 0;
                 return status;
             }
         }
@@ -1180,58 +1380,75 @@ g_io_channel_read_line_string (GIOChannel *channel,
 
       first_time = FALSE;
 
-      if (channel->line_term)
-        {
-          gchar *line_end = strstr (use_buf->str + checked_to, channel->line_term);
-          if (line_end)
-            {
-              line_length = line_end - use_buf->str;
-              break;
-            }
-        }
-      else /* auto detect */
-        {
-          gchar *newline_pos, *linefeed_pos;
-
-          newline_pos = strchr (use_buf->str + checked_to, '\n');
-          linefeed_pos = strchr (use_buf->str + checked_to, '\r');
+      lastchar = use_buf->str + strlen (use_buf->str);
 
-          g_assert (!newline_pos || !linefeed_pos || (newline_pos != linefeed_pos));
-
-          if (newline_pos && (!linefeed_pos || (linefeed_pos > newline_pos))) /* unix */
+      for (nextchar = use_buf->str + checked_to; nextchar < lastchar;
+           channel->encoding ? nextchar = g_utf8_next_char (nextchar) : nextchar++)
+        {
+          if (channel->line_term)
             {
-              line_length = newline_pos - use_buf->str;
-              line_term_len = 1;
-              break;
+              if (strncmp (channel->line_term, nextchar, line_term_len) == 0)
+                {
+                  line_length = nextchar - use_buf->str;
+                  got_term_len = line_term_len;
+                  goto done;
+                }
             }
-          else if (linefeed_pos &&
-                  (linefeed_pos < use_buf->str + use_buf->len - 1 ||
-                   status == G_IO_STATUS_EOF))
+          else /* auto detect */
             {
-              line_length = linefeed_pos - use_buf->str;
-              if (newline_pos && (newline_pos == linefeed_pos + 1)) /* dos */
-                line_term_len = 2;
-              else /* mac */
-               line_term_len = 1;
-              break;
+              switch (*nextchar)
+                {
+                  case '\n': /* unix */
+                    line_length = nextchar - use_buf->str;
+                    got_term_len = 1;
+                    goto done;
+                  case '\r': /* Warning: do not use with sockets */
+                    line_length = nextchar - use_buf->str;
+                    if ((nextchar == lastchar - 1) && (status != G_IO_STATUS_EOF)
+                       && (lastchar == use_buf->str + use_buf->len))
+                      goto read_again; /* Try to read more data */
+                    if ((nextchar < lastchar - 1) && (*(nextchar + 1) == '\n')) /* dos */
+                      got_term_len = 2;
+                    else /* mac */
+                      got_term_len = 1;
+                    goto done;
+                  case '\xe2': /* Unicode paragraph separator */
+                    if (strncmp ("\xe2\x80\xa9", nextchar, 3) == 0)
+                      {
+                        line_length = nextchar - use_buf->str;
+                        got_term_len = 3;
+                        goto done;
+                      }
+                    break;
+                  default: /* no match */
+                    break;
+                }
             }
         }
 
+      g_assert (nextchar == lastchar); /* Valid UTF-8, didn't overshoot */
+
       /* Also terminate on '\0' */
 
-      line_length = strlen (use_buf->str);
+      line_length = lastchar - use_buf->str;
       if (line_length < use_buf->len)
         {
-          line_term_len = 0;
+          got_term_len = 0;
           break;
         }
 
       /* Check for EOF */
 
-      if (status != G_IO_STATUS_NORMAL)
+      if (status == G_IO_STATUS_EOF)
         {
+          if (channel->encoding && 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;
+            }
           line_length = use_buf->len;
-          line_term_len = 0;
+          got_term_len = 0;
           break;
         }
 
@@ -1241,12 +1458,13 @@ g_io_channel_read_line_string (GIOChannel *channel,
        checked_to = 0;
     }
 
-  g_string_append_len (buffer, use_buf->str, line_length + line_term_len);
-  g_string_erase (use_buf, 0, line_length + line_term_len);
+done:
 
   if (terminator_pos)
     *terminator_pos = line_length;
 
+  *length = line_length + got_term_len;
+
   return G_IO_STATUS_NORMAL;
 }
 
@@ -1282,6 +1500,7 @@ g_io_channel_read_to_end (GIOChannel      *channel,
   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;
@@ -1297,15 +1516,33 @@ g_io_channel_read_to_end (GIOChannel    *channel,
     status = g_io_channel_fill_buffer (channel, error);
   while (status == G_IO_STATUS_NORMAL);
 
-  if ((status != G_IO_STATUS_EOF)
-    && (status != G_IO_STATUS_PARTIAL_CHARS))
+  if (status != G_IO_STATUS_EOF)
     return status;
 
-  if (channel->do_encode)
-    use_buf = &channel->encoded_read_buf;
+  if (channel->encoding)
+    {
+      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 ("");
+        }
+
+      use_buf = &channel->encoded_read_buf;
+    }
   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)->len;
 
@@ -1314,9 +1551,9 @@ g_io_channel_read_to_end (GIOChannel      *channel,
   else
     g_string_free (*use_buf, TRUE);
   
-  *use_buf = g_string_sized_new (channel->buf_size);
+  *use_buf = NULL;
 
-  return status;
+  return G_IO_STATUS_NORMAL;
 }
 
 /**
@@ -1349,6 +1586,7 @@ g_io_channel_read_chars (GIOChannel       *channel,
   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)
     {
@@ -1359,31 +1597,31 @@ g_io_channel_read_chars (GIOChannel     *channel,
 
   if (!channel->use_buffer)
     {
-      g_assert (channel->ready_to_read);
-      g_assert (channel->read_buf->len == 0);
+      g_assert (!channel->read_buf || channel->read_buf->len == 0);
 
       return channel->funcs->io_read (channel, buf, count, bytes_read, error);
     }
 
-  if (!channel->ready_to_read)
+  if (channel->encoding)
     {
-      status = g_io_channel_flush (channel, error);
-      if (status != G_IO_STATUS_NORMAL)
-        return status;
+      if (!channel->encoded_read_buf)
+        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
+      use_buf = channel->encoded_read_buf;
     }
-  g_assert (channel->ready_to_read);
-
-  if (channel->do_encode)
-    use_buf = channel->encoded_read_buf;
   else
-    use_buf = channel->read_buf;
+    {
+      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 < count && status == G_IO_STATUS_NORMAL)
-    {
-      status = g_io_channel_fill_buffer (channel, error);
-    }
+  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)
+     */
+    status = g_io_channel_fill_buffer (channel, error);
 
   switch (status)
     {
@@ -1391,19 +1629,55 @@ g_io_channel_read_chars (GIOChannel     *channel,
       g_assert (use_buf->len > 0);
       break;
     case G_IO_STATUS_EOF:
-    case G_IO_STATUS_PARTIAL_CHARS:
       if (use_buf->len == 0)
        {
          *bytes_read = 0;
-         return status;
+
+          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
+        {
+          *bytes_read = 0;
+          return G_IO_STATUS_AGAIN;
+        }
     default:
       *bytes_read = 0;
       return status;
     }
 
   *bytes_read = MIN (count, use_buf->len);
+
+  if (channel->encoding && *bytes_read > 0)
+    /* Don't validate for NULL encoding, binary safe */
+    {
+      gchar *nextchar, *prevchar = NULL;
+
+      nextchar = use_buf->str;
+
+      while (nextchar < use_buf->str + *bytes_read)
+        {
+          prevchar = nextchar;
+          nextchar = g_utf8_next_char (nextchar);
+        }
+
+      if (nextchar > use_buf->str + *bytes_read)
+        *bytes_read = prevchar - use_buf->str;
+
+      g_assert (*bytes_read > 0 || count < 6);
+    }
+
   memcpy (buf, use_buf->str, *bytes_read);
   g_string_erase (use_buf, 0, *bytes_read);
 
@@ -1416,15 +1690,14 @@ g_io_channel_read_chars (GIOChannel     *channel,
  * @buf: a buffer to write data from
  * @count: the size of the buffer. If -1, the buffer
  *         is taken to be a nul terminated string.
- * @bytes_written: The number of bytes written.
+ * @bytes_written: The number of bytes written. This can be nonzero
+ *                 even if the return value is not %G_IO_STATUS_NORMAL.
  * @error: A location to return an error of type #GConvertError
  *         or #GIOChannelError
  *
  * Replacement for g_io_channel_write() with the new API.
  *
- * Return value: a return value of %G_IO_STATUS_PARTIAL_CHARS
- *               indicates that @buf contains less than one
- *               complete UTF-8 character.
+ * Return value:
  **/
 GIOStatus
 g_io_channel_write_chars (GIOChannel   *channel,
@@ -1439,8 +1712,9 @@ g_io_channel_write_chars (GIOChannel      *channel,
   g_return_val_if_fail (bytes_written != 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_writeable, G_IO_STATUS_ERROR);
 
-  if (count < 0)
+  if ((count < 0) && buf)
     count = strlen (buf);
   
   if (count == 0)
@@ -1453,52 +1727,161 @@ g_io_channel_write_chars (GIOChannel   *channel,
 
   if (!channel->use_buffer)
     {
-      g_assert (channel->write_buf->len == 0);
+      g_assert (!channel->write_buf || channel->write_buf->len == 0);
+      g_assert (channel->partial_write_buf[0] == '\0');
       return channel->funcs->io_write (channel, buf, count, bytes_written, error);
     }
 
   /* General case */
 
-  if (channel->write_buf->len == 0)
+  if (channel->is_seekable && ((channel->read_buf && channel->read_buf->len > 0)
+    || (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)))
     {
-      /* Check to make sure the channel's writeable before we stick
-       * the first data in the buffer.
-       */
-      GIOFlags flags = g_io_channel_get_flags (channel);
-
-      if (!(flags & G_IO_FLAG_IS_WRITEABLE))
+      if (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)
         {
-          /* Mimic a failure of channel->funcs->io_write */
-          g_set_error (error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_BADF,
-                       _("Channel is not writeable"));
+          g_warning("Mixed reading and writing not allowed on encoded files");
           return G_IO_STATUS_ERROR;
         }
+      status = g_io_channel_seek_position (channel, 0, G_SEEK_CUR, error);
+      if (status != G_IO_STATUS_NORMAL)
+        return status;
     }
-  g_assert (channel->seekable_cached);
 
-  if (!channel->ready_to_write)
+  if (!channel->write_buf)
+    channel->write_buf = g_string_sized_new (channel->buf_size);
+
+  if (!channel->do_encode)
     {
-      if (channel->is_seekable)
+      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);
+
+              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);
+
+                  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 */
+                    }
+                }
+
+              skip = badchar - (channel->partial_write_buf + partial_chars);
+
+              buf += skip;
+              copy_bytes = count - skip;
+
+              g_string_append_len (channel->write_buf, channel->partial_write_buf,
+                                  badchar - channel->partial_write_buf);
+              channel->partial_write_buf[0] = '\0';
+            }
+          else
+            copy_bytes = count;
+
+          if (!g_utf8_validate (buf, copy_bytes, &badchar))
+            {
+              gunichar try_char;
+              gsize left_len = buf + copy_bytes - badchar;
+
+              try_char = g_utf8_get_char_validated (badchar, left_len);
+
+              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 */
+                }
+
+              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;
+            }
+        }
+      else
         {
-          if (channel->do_encode)
+          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;
+
+          if (channel->write_buf->len > 0)
             {
-              g_set_error (error, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_ENCODE_RW,
-                           _("Mixed reading and writing not allowed on encoded files"));
-              return G_IO_STATUS_ERROR;
+              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);
+            }
+          else
+            {
+              status = channel->funcs->io_write (channel, buf, copy_bytes, &wrote_bytes,
+                                                 error);
+              copy_bytes -= wrote_bytes;
+              buf += wrote_bytes;
             }
-          status = g_io_channel_seek_position (channel, 0, G_SEEK_CUR, error);
+
           if (status != G_IO_STATUS_NORMAL)
-            return status;
+            {
+              *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;
+            }
         }
-      channel->ready_to_write = TRUE;
-    }
-
-  if (channel->is_seekable)
-    channel->ready_to_read = FALSE;
 
-  if (!channel->do_encode)
-    {
-      g_string_append_len (channel->write_buf, buf, count);
+      g_string_append_len (channel->write_buf, buf, copy_bytes);
       *bytes_written = count;
     }
   else
@@ -1507,10 +1890,90 @@ g_io_channel_write_chars (GIOChannel    *channel,
       gchar *outbuf;
       gsize oldlen = channel->write_buf->len;
 
+      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);
+
+          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;
+
+          err = g_iconv (channel->write_cd, &inbuf, &partial_buf_size,
+                         &outbuf, &buf_space);
+
+          g_string_truncate (channel->write_buf, channel->write_buf->len - buf_space);
+
+          if (err == (size_t) -1)
+            {
+              if (errno == E2BIG)
+                goto redo;
+
+              if (errno == EINVAL)
+                {
+                  if (chars_in_buf == partial_buf_size)
+                    {
+                      /* 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;
+                    }
+
+                  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.
+                   */
+                }
+              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;
+                }
+            }
+
+          *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
         {
-         g_string_set_size (channel->write_buf, channel->write_buf->len + channel->buf_size);
-          buf_space = channel->buf_size;
+          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,
@@ -1524,51 +1987,73 @@ g_io_channel_write_chars (GIOChannel    *channel,
               switch (errno)
                {
                   case EINVAL:
-                    if (*bytes_written > 0)
-                      return G_IO_STATUS_NORMAL;
-                    else
-                      return G_IO_STATUS_PARTIAL_CHARS;
+                    {
+                      gint bytes_left = count - *bytes_written;
+
+                      g_assert (bytes_left < 6);
+
+                      memcpy (channel->partial_write_buf, buf, bytes_left);
+                      channel->partial_write_buf[bytes_left] = '\0';
+                    }
+                    *bytes_written = count;
+
+                    return G_IO_STATUS_NORMAL;
                   case E2BIG:
-                    break;
+                    {
+                      gsize wrote_bytes;
+
+                      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 (status != G_IO_STATUS_NORMAL)
+                        return status;
+                    }
+                    continue;
                   case EILSEQ:
-                    if (*bytes_written > 0)
-                      return G_IO_STATUS_NORMAL;
-                    else
-                      {
-                        g_set_error (error, G_CONVERT_ERROR,
-                         G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
-                          _("Invalid byte sequence in conversion input"));
-                        return G_IO_STATUS_ERROR;
-                      }
+                    g_set_error (error, G_CONVERT_ERROR,
+                      G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
+                      _("Invalid byte sequence in conversion input"));
+                    return G_IO_STATUS_ERROR;
                   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;
                     return G_IO_STATUS_ERROR;
                }
+
             }
         }
       while (bytes_to_go > 0);
     }
 
   if (channel->write_buf->len > channel->buf_size)
-    return g_io_channel_flush (channel, error);
+    {
+      gsize wrote_bytes;
+
+      status = channel->funcs->io_write (channel, channel->write_buf->str,
+                                         channel->write_buf->len, &wrote_bytes, error);
+
+      if (wrote_bytes > 0)
+        g_string_erase (channel->write_buf, 0, wrote_bytes);
+
+      return status;
+    }
   else
     return G_IO_STATUS_NORMAL;
 }
 
 /**
- * g_channel_error_quark:
+ * g_io_channel_error_quark:
  *
  * Return value: The quark used as %G_IO_CHANNEL_ERROR
  **/
 GQuark
-g_channel_error_quark (void)
+g_io_channel_error_quark (void)
 {
   static GQuark q = 0;
   if (q == 0)
-    q = g_quark_from_static_string ("g-channel-error-quark");
+    q = g_quark_from_static_string ("g-io-channel-error-quark");
 
   return q;
 }
index 8f2d341..950d25c 100644 (file)
@@ -47,26 +47,22 @@ typedef enum
   G_IO_ERROR_UNKNOWN
 } GIOError;
 
-#define G_IO_CHANNEL_ERROR g_channel_error_quark()
+#define G_IO_CHANNEL_ERROR g_io_channel_error_quark()
 
 typedef enum
 {
   /* Derived from errno */
-  G_IO_CHANNEL_ERROR_ACCES,
-  G_IO_CHANNEL_ERROR_BADF,
-  G_IO_CHANNEL_ERROR_DEADLK,
-  G_IO_CHANNEL_ERROR_FAULT,
+  G_IO_CHANNEL_ERROR_FBIG,
   G_IO_CHANNEL_ERROR_INVAL,
   G_IO_CHANNEL_ERROR_IO,
   G_IO_CHANNEL_ERROR_ISDIR,
-  G_IO_CHANNEL_ERROR_MFILE,
-  G_IO_CHANNEL_ERROR_NOLCK,
   G_IO_CHANNEL_ERROR_NOSPC,
-  G_IO_CHANNEL_ERROR_PERM,
+  G_IO_CHANNEL_ERROR_NXIO,
+  G_IO_CHANNEL_ERROR_OVERFLOW,
   G_IO_CHANNEL_ERROR_PIPE,
-  G_IO_CHANNEL_ERROR_SPIPE,
   /* Other */
-  G_IO_CHANNEL_ERROR_ENCODE_RW,
+  G_IO_CHANNEL_ERROR_PCHAR_FLUSH,
+  /* Unconverted partial UTF-8 chars in buffer during flush */
   G_IO_CHANNEL_ERROR_FAILED
 } GIOChannelError;
 
@@ -75,7 +71,6 @@ typedef enum
   G_IO_STATUS_ERROR,
   G_IO_STATUS_NORMAL,
   G_IO_STATUS_EOF,
-  G_IO_STATUS_PARTIAL_CHARS, /* like EOF, but with unconverted data left */
   G_IO_STATUS_AGAIN
 } GIOStatus;
 
@@ -99,7 +94,6 @@ typedef enum
 #define G_IO_CHANNEL_UNIX_LINE_TERM "\n"
 #define G_IO_CHANNEL_DOS_LINE_TERM "\r\n"
 #define G_IO_CHANNEL_MACINTOSH_LINE_TERM "\r"
-#define G_IO_CHANNEL_ENCODE_RAW "GIOChannelEncodeRaw"
 
 typedef enum
 {
@@ -113,41 +107,30 @@ typedef enum
   G_IO_FLAG_SET_MASK = G_IO_FLAG_APPEND | G_IO_FLAG_NONBLOCK,
 } GIOFlags;
 
-typedef enum
-{
-  G_IO_FILE_MODE_READ,
-  G_IO_FILE_MODE_WRITE,
-  G_IO_FILE_MODE_APPEND,
-  G_IO_FILE_MODE_READ_WRITE,
-  G_IO_FILE_MODE_READ_WRITE_TRUNCATE,
-  G_IO_FILE_MODE_READ_WRITE_APPEND,
-} GIOFileMode;
-
 struct _GIOChannel
 {
-  guint channel_flags;
   guint ref_count;
   GIOFuncs *funcs;
 
   gchar *encoding;
   GIConv read_cd;
   GIConv write_cd;
-  gchar *line_term;
+  gchar *line_term;            /* String which indicates the end of a line of text */
 
   gsize buf_size;
-  GString *read_buf;
-  GString *encoded_read_buf;
-  GString *write_buf;
-
-  /* Group the flags together to save memory */
-
-  gboolean use_buffer : 1;
-  gboolean do_encode : 1;
-  gboolean ready_to_read : 1;
-  gboolean ready_to_write : 1;
-  gboolean close_on_unref : 1;
-  gboolean seekable_cached : 1;
-  gboolean is_seekable : 1;
+  GString *read_buf;           /* Raw data from the channel */
+  GString *encoded_read_buf;    /* Channel data converted to UTF-8 */
+  GString *write_buf;          /* Data ready to be written to the file */
+  gchar partial_write_buf[6];  /* UTF-8 partial characters, null terminated */
+
+  /* Group the flags together, immediately after partial_write_buf, to save memory */
+
+  gboolean use_buffer : 1;     /* The encoding uses the buffers */
+  gboolean do_encode : 1;      /* The encoding uses the GIConv coverters */
+  gboolean close_on_unref : 1; /* Close the channel on final unref */
+  gboolean is_readable : 1;    /* Cached GIOFlag */
+  gboolean is_writeable : 1;   /* ditto */
+  gboolean is_seekable : 1;    /* ditto */
 };
 
 typedef gboolean (*GIOFunc) (GIOChannel   *source,
@@ -229,6 +212,9 @@ GIOFlags              g_io_channel_get_flags            (GIOChannel   *channel);
 void                  g_io_channel_set_line_term        (GIOChannel   *channel,
                                                         const gchar  *line_term);
 G_CONST_RETURN gchar* g_io_channel_get_line_term        (GIOChannel   *channel);
+void                 g_io_channel_set_buffered         (GIOChannel   *channel,
+                                                        gboolean      buffered);
+gboolean             g_io_channel_get_buffered         (GIOChannel   *channel);
 GIOStatus             g_io_channel_set_encoding         (GIOChannel   *channel,
                                                         const gchar  *encoding,
                                                         GError      **error);
@@ -265,13 +251,13 @@ GIOStatus   g_io_channel_seek_position    (GIOChannel   *channel,
                                           GSeekType     type,
                                           GError      **error);
 GIOChannel* g_io_channel_new_file         (const gchar  *filename,
-                                          GIOFileMode   mode,
+                                          const gchar  *mode,
                                           GError      **error);
 
 /* Error handling */
 
-GQuark          g_channel_error_quark      (void);
-GIOChannelError g_channel_error_from_errno (gint en);
+GQuark          g_io_channel_error_quark      (void);
+GIOChannelError g_io_channel_error_from_errno (gint en);
 
 /* On Unix, IO channels created with this function for any file
  * descriptor or socket.
index c7fe84d..3218e5c 100644 (file)
@@ -177,6 +177,9 @@ g_io_unix_read (GIOChannel *channel,
   GIOUnixChannel *unix_channel = (GIOUnixChannel *)channel;
   gssize result;
 
+  if (count > SSIZE_MAX) /* At least according to the Debian manpage for read */
+    count = SSIZE_MAX;
+
  retry:
   result = read (unix_channel->fd, buf, count);
 
@@ -196,7 +199,7 @@ g_io_unix_read (GIOChannel *channel,
 #endif
           default:
             g_set_error (err, G_IO_CHANNEL_ERROR,
-                         g_channel_error_from_errno (errno),
+                         g_io_channel_error_from_errno (errno),
                          strerror (errno));
             return G_IO_STATUS_ERROR;
         }
@@ -236,7 +239,7 @@ g_io_unix_write (GIOChannel  *channel,
 #endif
           default:
             g_set_error (err, G_IO_CHANNEL_ERROR,
-                         g_channel_error_from_errno (errno),
+                         g_io_channel_error_from_errno (errno),
                          strerror (errno));
             return G_IO_STATUS_ERROR;
         }
@@ -278,7 +281,7 @@ g_io_unix_seek (GIOChannel *channel,
   if (result < 0)
     {
       g_set_error (err, G_IO_CHANNEL_ERROR,
-                  g_channel_error_from_errno (errno),
+                  g_io_channel_error_from_errno (errno),
                   strerror (errno));
       return G_IO_STATUS_ERROR;
     }
@@ -296,7 +299,7 @@ g_io_unix_close (GIOChannel *channel,
   if (close (unix_channel->fd) < 0)
     {
       g_set_error (err, G_IO_CHANNEL_ERROR,
-                  g_channel_error_from_errno (errno),
+                  g_io_channel_error_from_errno (errno),
                   strerror (errno));
       return G_IO_STATUS_ERROR;
     }
@@ -351,19 +354,6 @@ static const glong g_io_unix_fcntl_posix_flags[] = {
 };
 #define G_IO_UNIX_NUM_FCNTL_FLAGS G_N_ELEMENTS (g_io_unix_fcntl_flags)
 
-static const GIOFlags g_io_unix_fcntl_flags_read_only[] = {
-  G_IO_FLAG_IS_READABLE,
-  G_IO_FLAG_IS_WRITEABLE,
-};
-static const glong g_io_unix_fcntl_posix_flags_read_only[] = {
-  O_RDONLY | O_RDWR,
-  O_WRONLY | O_RDWR,
-};
-/* Only need to map posix_flags -> flags for read only, not the
- * other way around, so this works.
- */
-#define G_IO_UNIX_NUM_FCNTL_FLAGS_READ_ONLY G_N_ELEMENTS (g_io_unix_fcntl_flags_read_only)
-
 static GIOStatus
 g_io_unix_set_flags (GIOChannel *channel,
                      GIOFlags    flags,
@@ -382,7 +372,7 @@ g_io_unix_set_flags (GIOChannel *channel,
   if (fcntl (unix_channel->fd, F_SETFL, fcntl_flags) == -1)
     {
       g_set_error (err, G_IO_CHANNEL_ERROR,
-                  g_channel_error_from_errno (errno),
+                  g_io_channel_error_from_errno (errno),
                   g_strerror (errno));
       return G_IO_STATUS_ERROR;
     }
@@ -397,103 +387,200 @@ g_io_unix_get_flags (GIOChannel *channel)
   glong fcntl_flags;
   gint loop;
   GIOUnixChannel *unix_channel = (GIOUnixChannel *) channel;
-  struct stat buffer;
 
   fcntl_flags = fcntl (unix_channel->fd, F_GETFL);
+
   if (fcntl_flags == -1)
     {
       g_warning (G_STRLOC "Error while getting flags for FD: %s (%d)\n",
                 g_strerror (errno), errno);
       return 0;
     }
-  if (!channel->seekable_cached)
-    {
-      channel->seekable_cached = TRUE;
-
-      /* I'm not sure if fstat on a non-file (e.g., socket) works
-       * it should be safe to sat if it fails, the fd isn't seekable.
-       */
-      if (fstat (unix_channel->fd, &buffer) == -1 ||
-         !S_ISREG (buffer.st_mode))
-       channel->is_seekable = FALSE;
-      else
-       channel->is_seekable = TRUE;
-    }
 
   for (loop = 0; loop < G_IO_UNIX_NUM_FCNTL_FLAGS; loop++)
     if (fcntl_flags & g_io_unix_fcntl_posix_flags[loop])
       flags |= g_io_unix_fcntl_flags[loop];
 
-  for (loop = 0; loop < G_IO_UNIX_NUM_FCNTL_FLAGS_READ_ONLY; loop++)
-    if (fcntl_flags & g_io_unix_fcntl_posix_flags_read_only[loop])
-      flags |= g_io_unix_fcntl_flags_read_only[loop];
-
-  if (channel->is_seekable)
-    flags |= G_IO_FLAG_IS_SEEKABLE;
-
   return flags;
 }
 
 GIOChannel *
 g_io_channel_new_file (const gchar *filename,
-                       GIOFileMode  mode,
+                       const gchar *mode,
                        GError     **error)
 {
-  FILE *f;
-  int fid;
-  gchar *mode_name;
+  int fid, flags;
   GIOChannel *channel;
+  enum { /* Cheesy hack */
+    MODE_R = 1 << 0,
+    MODE_W = 1 << 1,
+    MODE_A = 1 << 2,
+    MODE_PLUS = 1 << 3,
+  } mode_num;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+  g_return_val_if_fail (mode != NULL, NULL);
+  g_return_val_if_fail ((error == NULL) || (*error == NULL), NULL);
+
+  switch (mode[0])
+    {
+      case 'r':
+        mode_num = MODE_R;
+        break;
+      case 'w':
+        mode_num = MODE_W;
+        break;
+      case 'a':
+        mode_num = MODE_A;
+        break;
+      default:
+        g_warning ("Invalid GIOFileMode %s.\n", mode);
+        return NULL;
+    }
+
+  switch (mode[1])
+    {
+      case '\0':
+        break;
+      case '+':
+        if (mode[2] == '\0')
+          {
+            mode_num |= MODE_PLUS;
+            break;
+          }
+        /* Fall through */
+      default:
+        g_warning ("Invalid GIOFileMode %s.\n", mode);
+        return NULL;
+    }
 
-  switch (mode)
+  switch (mode_num)
     {
-      case G_IO_FILE_MODE_READ:
-        mode_name = "r";
+      case MODE_R:
+        flags = O_RDONLY;
         break;
-      case G_IO_FILE_MODE_WRITE:
-        mode_name = "w";
+      case MODE_W:
+        flags = O_WRONLY | O_TRUNC | O_CREAT;
         break;
-      case G_IO_FILE_MODE_APPEND:
-        mode_name = "a";
+      case MODE_A:
+        flags = O_WRONLY | O_APPEND | O_CREAT;
         break;
-      case G_IO_FILE_MODE_READ_WRITE:
-        mode_name = "r+";
+      case MODE_R | MODE_PLUS:
+        flags = O_RDWR;
         break;
-      case G_IO_FILE_MODE_READ_WRITE_TRUNCATE:
-        mode_name = "w+";
+      case MODE_W | MODE_PLUS:
+        flags = O_RDWR | O_TRUNC | O_CREAT;
         break;
-      case G_IO_FILE_MODE_READ_WRITE_APPEND:
-        mode_name = "a+";
+      case MODE_A | MODE_PLUS:
+        flags = O_RDWR | O_APPEND | O_CREAT;
         break;
       default:
-        g_warning ("Invalid GIOFileMode %i.\n", mode);
-        return NULL;
+        g_assert_not_reached ();
+        flags = 0;
     }
 
-  f = fopen (filename, mode_name);
-  if (!f)
+  fid = open (filename, flags);
+  if (fid < 0)
     {
       g_set_error (error, G_FILE_ERROR,
                    g_file_error_from_errno (errno),
                    strerror (errno));
       return (GIOChannel *)NULL;
     }
-    
-  fid = fileno (f);
 
-  channel = g_io_channel_unix_new (fid);
+  channel = (GIOChannel *) g_new (GIOUnixChannel, 1);
+
   channel->close_on_unref = TRUE;
+  channel->is_seekable = TRUE;
+
+  switch (mode_num)
+    {
+      case MODE_R:
+        channel->is_readable = TRUE;
+        channel->is_writeable = FALSE;
+        break;
+      case MODE_W:
+      case MODE_A:
+        channel->is_readable = FALSE;
+        channel->is_writeable = TRUE;
+        break;
+      case MODE_R | MODE_PLUS:
+      case MODE_W | MODE_PLUS:
+      case MODE_A | MODE_PLUS:
+        channel->is_readable = TRUE;
+        channel->is_writeable = TRUE;
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  g_io_channel_init (channel);
+  channel->funcs = &unix_channel_funcs;
+
+  ((GIOUnixChannel *) channel)->fd = fid;
   return channel;
 }
 
 GIOChannel *
 g_io_channel_unix_new (gint fd)
 {
+  struct stat buffer;
   GIOUnixChannel *unix_channel = g_new (GIOUnixChannel, 1);
   GIOChannel *channel = (GIOChannel *)unix_channel;
+  int flags;
 
   g_io_channel_init (channel);
   channel->funcs = &unix_channel_funcs;
 
+  /* I'm not sure if fstat on a non-file (e.g., socket) works
+   * it should be safe to say if it fails, the fd isn't seekable.
+   */
+  /* Newer UNIX versions support S_ISSOCK(), fstat() will probably
+   * succeed in most cases.
+   */
+  if (fstat (unix_channel->fd, &buffer) == 0)
+    channel->is_seekable = S_ISREG (buffer.st_mode) || S_ISCHR (buffer.st_mode)
+                           || S_ISBLK (buffer.st_mode);
+  else /* Assume not seekable */
+    channel->is_seekable = FALSE;
+
+  flags = fcntl (fd, F_GETFL);
+
+  if (flags != -1)
+    {
+      /* Don't know if fcntl flags overlap, be careful */
+
+      if (flags & O_WRONLY)
+        {
+          channel->is_readable = FALSE;
+          channel->is_writeable = TRUE;
+        }
+      else if (flags & O_RDWR)
+        channel->is_readable = channel->is_writeable = TRUE;
+#if O_RDONLY == 0
+      else /* O_RDONLY defined as zero on linux (elsewhere?) */
+        {
+          channel->is_readable = TRUE;
+          channel->is_writeable = FALSE;
+        }
+#else /* O_RDONLY == 0 */
+      else if (flags & O_RDONLY)
+        {
+          channel->is_readable = TRUE;
+          channel->is_writeable = FALSE;
+        }
+      else
+        channel->is_readable = channel->is_writeable = FALSE;
+#endif /* O_RDONLY == 0 */
+    }
+  else
+    {
+      g_warning (G_STRLOC "Error while getting flags for FD: %s (%d)\n",
+                 g_strerror (errno), errno);
+      g_free (channel);
+      return NULL;
+    }
+
   unix_channel->fd = fd;
   return channel;
 }
index 1551c6c..ca8c997 100644 (file)
@@ -699,20 +699,22 @@ g_io_win32_msg_write (GIOChannel  *channel,
 
 static GIOStatus
 g_io_win32_no_seek (GIOChannel *channel,
-                   gint        offset,
+                   glong       offset,
                    GSeekType   type,
                    GError     **err)
 {
-  g_set_error(err, G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_SPIPE,
-    _("Seeking not allowed on this type of channel"));
+  g_assert_not_reached ();
 
   return G_IO_STATUS_ERROR;
 }
 
-static void
-g_io_win32_msg_close (GIOChannel *channel)
+static GIOStatus
+g_io_win32_msg_close (GIOChannel *channel,
+                     GError    **err)
 {
   /* Nothing to be done. Or should we set hwnd to some invalid value? */
+
+  return G_IO_STATUS_NORMAL;
 }
 
 static void
@@ -794,7 +796,7 @@ g_io_win32_fd_read (GIOChannel *channel,
 #endif
           default:
             g_set_error (err, G_IO_CHANNEL_ERROR,
-                         g_channel_error_from_errno (errno),
+                         g_io_channel_error_from_errno (errno),
                          strerror (errno));
             return G_IO_STATUS_ERROR;
         }
@@ -832,7 +834,7 @@ g_io_win32_fd_write (GIOChannel  *channel,
 #endif
           default:
             g_set_error (err, G_IO_CHANNEL_ERROR,
-                         g_channel_error_from_errno (errno),
+                         g_io_channel_error_from_errno (errno),
                          strerror (errno));
             return G_IO_STATUS_ERROR;
         }
@@ -845,7 +847,7 @@ g_io_win32_fd_write (GIOChannel  *channel,
 
 static GIOStatus
 g_io_win32_fd_seek (GIOChannel *channel,
-                   gint        offset,
+                   glong       offset,
                    GSeekType   type,
                    GError    **err)
 {
@@ -874,7 +876,7 @@ g_io_win32_fd_seek (GIOChannel *channel,
   if (result < 0)
     {
       g_set_error (err, G_IO_CHANNEL_ERROR,
-                  g_channel_error_from_errno (errno),
+                  g_io_channel_error_from_errno (errno),
                   strerror (errno));
       return G_IO_STATUS_ERROR;
     }
@@ -882,8 +884,9 @@ g_io_win32_fd_seek (GIOChannel *channel,
   return G_IO_STATUS_NORMAL;
 }
 
-static void
-g_io_win32_fd_close (GIOChannel *channel)
+static GIOStatus
+g_io_win32_fd_close (GIOChannel *channel,
+                    GError    **err)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
   
@@ -912,6 +915,10 @@ g_io_win32_fd_close (GIOChannel *channel)
       win32_channel->fd = -1;
     }
   UNLOCK (win32_channel->mutex);
+
+  /* FIXME error detection? */
+
+  return G_IO_STATUS_NORMAL;
 }
 
 static GSource *
@@ -935,7 +942,7 @@ g_io_win32_sock_read (GIOChannel *channel,
   if (win32_channel->debug)
     g_print ("g_io_win32_sock_read: sockfd:%d count:%d\n",
             win32_channel->fd, count);
-  
+repeat: 
   result = recv (win32_channel->fd, buf, count, 0);
 
   if (win32_channel->debug)
@@ -954,7 +961,7 @@ g_io_win32_sock_read (GIOChannel *channel,
           return G_IO_STATUS_AGAIN;
 #ifdef WE_NEED_TO_HANDLE_WSAEINTR /* not anymore with wsock2 ? */
        case WSAEINTR:
-          return G_IO_STATUS_INTR;
+          goto repeat;
 #endif
        default:
          error = G_IO_CHANNEL_ERROR_FAILED;
@@ -986,7 +993,7 @@ g_io_win32_sock_write (GIOChannel  *channel,
   if (win32_channel->debug)
     g_print ("g_io_win32_sock_write: sockfd:%d count:%d\n",
             win32_channel->fd, count);
-  
+repeat:
   result = send (win32_channel->fd, buf, count, 0);
   
   if (win32_channel->debug)
@@ -1005,7 +1012,7 @@ g_io_win32_sock_write (GIOChannel  *channel,
           return G_IO_STATUS_AGAIN;
 #ifdef WE_NEED_TO_HANDLE_WSAEINTR /* not anymore with wsock2 ? */
        case WSAEINTR:
-         return G_IO_STATUS_INTR;
+          goto repeat;
 #endif
        default:
          error = G_IO_CHANNEL_ERROR_FAILED;
@@ -1023,8 +1030,9 @@ g_io_win32_sock_write (GIOChannel  *channel,
     }
 }
 
-static void
-g_io_win32_sock_close (GIOChannel *channel)
+static GIOStatus
+g_io_win32_sock_close (GIOChannel *channel,
+                      GError    **err)
 {
   GIOWin32Channel *win32_channel = (GIOWin32Channel *)channel;
 
@@ -1049,6 +1057,10 @@ g_io_win32_sock_close (GIOChannel *channel)
     win32_channel->fd = -1;
   }
   UNLOCK(win32_channel->mutex);
+
+  /* FIXME error detection? */
+
+  return G_IO_STATUS_NORMAL;
 }
 
 static GSource *
@@ -1135,6 +1147,10 @@ g_io_channel_win32_new_messages (guint hwnd)
   win32_channel->type = G_IO_WIN32_WINDOWS_MESSAGES;
   win32_channel->hwnd = (HWND) hwnd;
 
+  /* FIXME set is_readable, is_writeable */
+
+  channel->is_seekable = FALSE;
+
   return channel;
 }
 
@@ -1162,6 +1178,10 @@ g_io_channel_win32_new_fd (gint fd)
   win32_channel->type = G_IO_WIN32_FILE_DESC;
   win32_channel->fd = fd;
 
+  /* FIXME set is_readable, is_writeable */
+
+  channel->is_seekable = TRUE;
+
   return channel;
 }
 
@@ -1187,6 +1207,10 @@ g_io_channel_win32_new_socket (int socket)
   win32_channel->type = G_IO_WIN32_SOCKET;
   win32_channel->fd = socket;
 
+  /* FIXME set is_readable, is_writeable */
+
+  channel->is_seekable = FALSE;
+
   return channel;
 }
 
diff --git a/tests/iochannel-test-infile b/tests/iochannel-test-infile
new file mode 100644 (file)
index 0000000..86e24ee
--- /dev/null
@@ -0,0 +1,5 @@
+Line one
+Line two
+Line three
+/* Hello */
+\x1234\x567890\x6666
index 18ea01d..4832fb2 100644 (file)
@@ -19,43 +19,42 @@ gint main (gint argc, gchar * argv[])
     GIOStatus status;
     GIOFlags flags;
 
-    if (argc < 2)
-    {
-        g_print( "Usage: foo in-file\n" );
-        exit (1);
-    }
-
     setbuf(stdout, NULL); /* For debugging */
 
-    gio_r = g_io_channel_new_file (argv[1], G_IO_FILE_MODE_READ, &gerr);
-    if(gerr) {
-      g_warning(gerr->message);
-      g_warning("Unable to open file %s", argv[1]);
-      g_free(gerr);
-      return 0;
-    }
-    gio_w = g_io_channel_new_file( "/var/tmp/fooOut.txt", G_IO_FILE_MODE_WRITE, &gerr);
-    if(gerr) {
-      g_warning(gerr->message);
-      g_free(gerr);
-      return 0;
-    }
+    gio_r = g_io_channel_new_file ("iochannel-test-infile", "r", &gerr);
+    if (gerr)
+      {
+        g_warning(gerr->message);
+        g_warning("Unable to open file %s", "iochannel-test-infile");
+        g_free(gerr);
+        return 0;
+      }
+    gio_w = g_io_channel_new_file( "iochannel-test-outfile", "w", &gerr);
+    if (gerr)
+      {
+        g_warning(gerr->message);
+        g_warning("Unable to open file %s", "iochannel-test-outfile");
+        g_free(gerr);
+        return 0;
+      }
 
     g_io_channel_set_encoding (gio_r, encoding, &gerr);
-    if(gerr) {
-      g_warning(gerr->message);
-      g_free(gerr);
-      return 0;
-    }
+    if (gerr)
+      {
+        g_warning(gerr->message);
+        g_free(gerr);
+        return 0;
+      }
     
     g_io_channel_set_buffer_size (gio_r, BUFFER_SIZE);
 
     status = g_io_channel_set_flags (gio_r, G_IO_FLAG_NONBLOCK, &gerr);
-    if(status == G_IO_STATUS_ERROR) {
-      g_warning(gerr->message);
-      g_error_free(gerr);
-      gerr = NULL;
-    }
+    if (status == G_IO_STATUS_ERROR)
+      {
+        g_warning(gerr->message);
+        g_error_free(gerr);
+        gerr = NULL;
+      }
     flags = g_io_channel_get_flags (gio_r);
     block = ! (flags & G_IO_FLAG_NONBLOCK);
     if (block)
@@ -106,9 +105,6 @@ gint main (gint argc, gchar * argv[])
       {
         case G_IO_STATUS_EOF:
           break;
-        case G_IO_STATUS_PARTIAL_CHARS:
-          g_warning ("Partial characters at the end of input.");
-          break;
         case G_IO_STATUS_ERROR:
           g_warning (gerr->message);
           g_error_free (gerr);
@@ -123,11 +119,12 @@ gint main (gint argc, gchar * argv[])
       status = g_io_channel_flush (gio_w, &gerr);
     while (status == G_IO_STATUS_AGAIN);
 
-    if(status == G_IO_STATUS_ERROR) {
-      g_warning(gerr->message);
-      g_error_free(gerr);
-      gerr = NULL;
-    }
+    if (status == G_IO_STATUS_ERROR)
+      {
+        g_warning(gerr->message);
+        g_error_free(gerr);
+        gerr = NULL;
+      }
 
     g_print ("read %d bytes, wrote %d bytes\n", rlength, wlength);
 
index 75d3f59..8832d1f 100644 (file)
@@ -42,7 +42,7 @@ int main (int argc, char **argv)
 
   if (argc == 2)
     {
-      in = g_io_channel_new_file (argv[1], G_IO_FILE_MODE_READ, &error);
+      in = g_io_channel_new_file (argv[1], "r", &error);
       if (!in)
        {
          fprintf (stderr, "Cannot open %s: %s\n", argv[1], error->message);
index 7ea46ed..ec9574e 100644 (file)
@@ -141,7 +141,7 @@ int main (int argc, char **argv)
   if (argc == 3)
     line_to_do = atoi(argv[2]);
 
-  in = g_io_channel_new_file (argv[1], G_IO_FILE_MODE_READ, &error);
+  in = g_io_channel_new_file (argv[1], "r", &error);
   if (!in)
     {
       fprintf (stderr, "Cannot open %s: %s\n", argv[1], error->message);