Guard against the POSIX allowed behavior where access (file, X_OK)
[platform/upstream/glib.git] / glib / giochannel.c
index 545ef24..68ed280 100644 (file)
@@ -32,7 +32,6 @@
  */
 
 #include "config.h"
-#include "giochannel.h"
 
 #include <string.h>
 #include <errno.h>
@@ -45,6 +44,8 @@
 
 #include "glib.h"
 
+#include "giochannel.h"
+
 #include "glibintl.h"
 
 #define G_IO_NICE_BUF_SIZE     1024
@@ -76,6 +77,7 @@ g_io_channel_init (GIOChannel *channel)
   channel->ref_count = 1;
   channel->encoding = g_strdup ("UTF-8");
   channel->line_term = NULL;
+  channel->line_term_len = 0;
   channel->buf_size = G_IO_NICE_BUF_SIZE;
   channel->read_cd = (GIConv) -1;
   channel->write_cd = (GIConv) -1;
@@ -113,7 +115,8 @@ g_io_channel_unref (GIOChannel *channel)
         g_iconv_close (channel->read_cd);
       if (channel->write_cd != (GIConv) -1)
         g_iconv_close (channel->write_cd);
-      g_free (channel->line_term);
+      if (channel->line_term)
+        g_free (channel->line_term);
       if (channel->read_buf)
         g_string_free (channel->read_buf, TRUE);
       if (channel->write_buf)
@@ -158,10 +161,11 @@ g_io_error_get_from_g_error (GIOStatus status,
  * @count: the number of bytes to read from the #GIOChannel.
  * @bytes_read: returns the number of bytes actually read. 
  * 
- * Reads data from a #GIOChannel. This function is depricated. New code should
- * use g_io_channel_read_chars() instead.
+ * Reads data from a #GIOChannel. 
  * 
  * Return value: %G_IO_ERROR_NONE if the operation was successful. 
+ *
+ * Deprecated: Use g_io_channel_read_chars() instead.
  **/
 GIOError 
 g_io_channel_read (GIOChannel *channel, 
@@ -193,10 +197,11 @@ g_io_channel_read (GIOChannel *channel,
  * @count: the number of bytes to write.
  * @bytes_written:  the number of bytes actually written.
  * 
- * Writes data to a #GIOChannel. This function is depricated. New code should
- * use g_io_channel_write_chars() instead.
+ * Writes data to a #GIOChannel. 
  * 
  * Return value:  %G_IO_ERROR_NONE if the operation was successful.
+ *
+ * Deprecated: Use g_io_channel_write_chars() instead.
  **/
 GIOError 
 g_io_channel_write (GIOChannel  *channel, 
@@ -230,14 +235,15 @@ g_io_channel_write (GIOChannel  *channel,
  *        file).
  * 
  * Sets the current position in the #GIOChannel, similar to the standard library
- * function fseek(). This function is depricated. New code should
- * use g_io_channel_seek_position() instead.
+ * function <function>fseek()</function>. 
  * 
  * Return value: %G_IO_ERROR_NONE if the operation was successful.
+ *
+ * Deprecated: Use g_io_channel_seek_position() instead.
  **/
 GIOError 
 g_io_channel_seek  (GIOChannel   *channel,
-                   glong         offset, 
+                   gint64        offset, 
                    GSeekType     type)
 {
   GError *err = NULL;
@@ -276,8 +282,8 @@ g_io_channel_seek  (GIOChannel   *channel,
  * g_io_channel_new_file:
  * @filename: A string containing the name of a file.
  * @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.
+ *        the same meaning as in <function>fopen()</function>.
+ * @error: A location to return an error of type %G_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,
@@ -294,9 +300,9 @@ g_io_channel_seek  (GIOChannel   *channel,
  * 
  * Close an IO channel. Any pending data to be written will be
  * flushed, ignoring errors. The channel will not be freed until the
- * last reference is dropped using g_io_channel_unref(). This
- * function is deprecated: you should use g_io_channel_shutdown()
- * instead.
+ * last reference is dropped using g_io_channel_unref(). 
+ *
+ * Deprecated: Use g_io_channel_shutdown() instead.
  **/
 void
 g_io_channel_close (GIOChannel *channel)
@@ -328,10 +334,10 @@ g_io_channel_close (GIOChannel *channel)
  * @err: location to store a #GIOChannelError
  * 
  * Close an IO channel. Any pending data to be written will be
- * flushed. The channel will not be freed until the
+ * flushed if @flush is %TRUE. The channel will not be freed until the
  * last reference is dropped using g_io_channel_unref().
  *
- * Return value:
+ * Return value: the status of the operation.
  **/
 GIOStatus
 g_io_channel_shutdown (GIOChannel *channel,
@@ -344,27 +350,35 @@ g_io_channel_shutdown (GIOChannel *channel,
   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 && channel->write_buf->len > 0)
+  if (channel->write_buf && channel->write_buf->len > 0)
     {
-      GIOFlags flags;
+      if (flush)
+        {
+          GIOFlags flags;
       
-      /* Set the channel to blocking, to avoid a busy loop
-       */
-      flags = g_io_channel_get_flags (channel);
-      /* 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);
+          /* Set the channel to blocking, to avoid a busy loop
+           */
+          flags = g_io_channel_get_flags (channel);
+          /* Ignore any errors here, they're irrelevant */
+          g_io_channel_set_flags (channel, flags & ~G_IO_FLAG_NONBLOCK, NULL);
 
-      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';
+          result = g_io_channel_flush (channel, &tmperr);
         }
+      else
+        result = G_IO_STATUS_NORMAL;
+
+      g_string_truncate(channel->write_buf, 0);
     }
   else
     result = G_IO_STATUS_NORMAL;
 
+  if (channel->partial_write_buf[0] != '\0')
+    {
+      if (flush)
+        g_warning ("Partial character at end of write buffer not flushed.\n");
+      channel->partial_write_buf[0] = '\0';
+    }
+
   status = channel->funcs->io_close (channel, err);
 
   channel->close_on_unref = FALSE; /* Because we already did */
@@ -479,9 +493,10 @@ g_io_add_watch (GIOChannel    *channel,
  * g_io_channel_get_buffer_condition:
  * @channel: A #GIOChannel
  *
- * This function returns a #GIOCondition depending on the status of the
+ * This function returns a #GIOCondition depending on whether there
+ * is data to be read/space to write data in the
  * internal buffers in the #GIOChannel. Only the flags %G_IO_IN and
- * %G_IO_OUT will be set.
+ * %G_IO_OUT may be set.
  *
  * Return value: A #GIOCondition
  **/
@@ -509,9 +524,11 @@ g_io_channel_get_buffer_condition (GIOChannel *channel)
 
 /**
  * g_io_channel_error_from_errno:
- * @en: An errno error number, e.g. EINVAL
+ * @en: an <literal>errno</literal> error number, e.g. %EINVAL.
+ *
+ * Converts an <literal>errno</literal> error number to a #GIOChannelError.
  *
- * Return value: A #GIOChannelError error number, e.g. %G_IO_CHANNEL_ERROR_INVAL
+ * Return value: a #GIOChannelError error number, e.g. %G_IO_CHANNEL_ERROR_INVAL.
  **/
 GIOChannelError
 g_io_channel_error_from_errno (gint en)
@@ -587,7 +604,7 @@ g_io_channel_error_from_errno (gint en)
  * @channel: a #GIOChannel
  * @size: the size of the buffer. 0 == pick a good size
  *
- * Set the buffer size.
+ * Sets the buffer size.
  **/  
 void
 g_io_channel_set_buffer_size (GIOChannel       *channel,
@@ -608,7 +625,7 @@ g_io_channel_set_buffer_size (GIOChannel    *channel,
  * g_io_channel_get_buffer_size:
  * @channel: a #GIOChannel
  *
- * Get the buffer size.
+ * Gets the buffer size.
  *
  * Return value: the size of the buffer.
  **/  
@@ -624,26 +641,39 @@ g_io_channel_get_buffer_size (GIOChannel  *channel)
  * g_io_channel_set_line_term:
  * @channel: a #GIOChannel
  * @line_term: The line termination string. Use %NULL for auto detect.
+ *             Auto detection breaks on "\n", "\r\n", "\r", "\0", and
+ *             the Unicode paragraph separator. Auto detection should
+ *             not be used for anything other than file-based channels.
+ * @length: The length of the termination string. If -1 is passed, the
+ *          string is assumed to be nul-terminated. This option allows
+ *          termination strings with embeded nuls.
  *
  * This sets the string that #GIOChannel uses to determine
  * where in the file a line break occurs.
  **/
 void
 g_io_channel_set_line_term (GIOChannel *channel,
-                            const gchar        *line_term)
+                            const gchar        *line_term,
+                           gint         length)
 {
   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_return_if_fail (line_term == NULL || length != 0); /* Disallow "" */
 
-  g_free (channel->line_term);
-  channel->line_term = g_strdup (line_term);
+  if (line_term == NULL)
+    length = 0;
+  else if (length < 0)
+    length = strlen (line_term);
+
+  if (channel->line_term)
+    g_free (channel->line_term);
+  channel->line_term = line_term ? g_memdup (line_term, length) : NULL;
+  channel->line_term_len = length;
 }
 
 /**
  * g_io_channel_get_line_term:
  * @channel: a #GIOChannel
+ * @length: a location to return the length of the line terminator
  *
  * This returns the string that #GIOChannel uses to determine
  * where in the file a line break occurs. A value of %NULL
@@ -653,20 +683,26 @@ g_io_channel_set_line_term (GIOChannel    *channel,
  *   is owned by GLib and must not be freed.
  **/
 G_CONST_RETURN gchar*
-g_io_channel_get_line_term (GIOChannel *channel)
+g_io_channel_get_line_term (GIOChannel *channel,
+                           gint        *length)
 {
   g_return_val_if_fail (channel != NULL, 0);
 
+  if (length)
+    *length = channel->line_term_len;
+
   return channel->line_term;
 }
 
 /**
  * g_io_channel_set_flags:
- * @channel: a #GIOChannel
- * @flags: the flags to set on the channel
- * @error: A location to return an error of type #GIOChannelError
+ * @channel: a #GIOChannel.
+ * @flags: the flags to set on the IO channel.
+ * @error: A location to return an error of type #GIOChannelError.
+ *
+ * Sets the (writeable) flags in @channel to (@flags & %G_IO_CHANNEL_SET_MASK).
  *
- * Return value:
+ * Return value: the status of the operation. 
  **/
 GIOStatus
 g_io_channel_set_flags (GIOChannel *channel,
@@ -689,6 +725,13 @@ g_io_channel_set_flags (GIOChannel *channel,
  * Gets the current flags for a #GIOChannel, including read-only
  * flags such as %G_IO_FLAG_IS_READABLE.
  *
+ * The values of the flags %G_IO_FLAG_IS_READABLE and %G_IO_FLAG_IS_WRITEABLE
+ * are cached for internal use by the channel when it is created.
+ * If they should change at some later point (e.g. partial shutdown
+ * of a socket with the UNIX <function>shutdown()</function> function), the user
+ * should immediately call g_io_channel_get_flags () to update
+ * the internal values of these flags.
+ *
  * Return value: the flags which are set on the channel
  **/
 GIOFlags
@@ -696,7 +739,7 @@ g_io_channel_get_flags (GIOChannel *channel)
 {
   GIOFlags flags;
 
-  g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
+  g_return_val_if_fail (channel != NULL, 0);
 
   flags = (* channel->funcs->io_get_flags) (channel);
 
@@ -713,21 +756,62 @@ g_io_channel_get_flags (GIOChannel *channel)
 }
 
 /**
+ * g_io_channel_set_close_on_unref:
+ * @channel: a #GIOChannel
+ * @do_close: Whether to close the channel on the final unref of
+ *            the GIOChannel data structure. The default value of
+ *            this is %TRUE for channels created by g_io_channel_new_file (),
+ *            and %FALSE for all other channels.
+ *
+ * Setting this flag to %TRUE for a channel you have already closed
+ * can cause problems.
+ **/
+void
+g_io_channel_set_close_on_unref        (GIOChannel *channel,
+                                gboolean    do_close)
+{
+  g_return_if_fail (channel != NULL);
+
+  channel->close_on_unref = do_close;
+}
+
+/**
+ * g_io_channel_get_close_on_unref:
+ * @channel: a #GIOChannel.
+ *
+ * Returns whether the file/socket/whatever associated with @channel
+ * will be closed when @channel receives its final unref and is
+ * destroyed. The default value of this is %TRUE for channels created
+ * by g_io_channel_new_file (), and %FALSE for all other channels.
+ *
+ * Return value: Whether the channel will be closed on the final unref of
+ *               the GIOChannel data structure.
+ **/
+gboolean
+g_io_channel_get_close_on_unref        (GIOChannel *channel)
+{
+  g_return_val_if_fail (channel != NULL, FALSE);
+
+  return channel->close_on_unref;
+}
+
+/**
  * g_io_channel_seek_position:
  * @channel: a #GIOChannel
  * @offset: The offset in bytes from the position specified by @type
- * @type: a #GSeekType. The type %G_SEEK_CUR is only allowed if
- *        the channel has the default encoding or the
- *        encoding %G_IO_CHANNEL_ENCODE_RAW for raw file access.
+ * @type: a #GSeekType. The type %G_SEEK_CUR is only allowed in those
+ *                      cases where a call to g_io_channel_set_encoding ()
+ *                      is allowed. See the documentation for
+ *                      g_io_channel_set_encoding () for details.
  * @error: A location to return an error of type #GIOChannelError
  *
  * Replacement for g_io_channel_seek() with the new API.
  *
- * Return value:
+ * Return value: the status of the operation.
  **/
 GIOStatus
 g_io_channel_seek_position     (GIOChannel* channel,
-                                glong       offset,
+                                gint64      offset,
                                 GSeekType   type,
                                 GError    **error)
 {
@@ -758,7 +842,7 @@ g_io_channel_seek_position  (GIOChannel* channel,
             offset -= channel->read_buf->len;
           if (channel->encoded_read_buf)
             {
-              g_assert (channel->encoded_read_buf->len == 0 && !channel->do_encode);
+              g_assert (channel->encoded_read_buf->len == 0 || !channel->do_encode);
 
               /* If there's anything here, it's because the encoding is UTF-8,
                * so we can just subtract the buffer length, the same as for
@@ -818,11 +902,11 @@ g_io_channel_seek_position        (GIOChannel* channel,
  * @channel: a #GIOChannel
  * @error: location to store an error of type #GIOChannelError
  *
- * Flush the write buffer for the GIOChannel.
+ * Flushes the write buffer for the GIOChannel.
  *
  * Return value: the status of the operation: One of
- *   G_IO_CHANNEL_NORMAL, G_IO_CHANNEL_AGAIN, or
- *   G_IO_CHANNEL_ERROR.
+ *   #G_IO_CHANNEL_NORMAL, #G_IO_CHANNEL_AGAIN, or
+ *   #G_IO_CHANNEL_ERROR.
  **/
 GIOStatus
 g_io_channel_flush (GIOChannel *channel,
@@ -842,9 +926,9 @@ g_io_channel_flush (GIOChannel      *channel,
       g_assert (this_time > 0);
 
       status = channel->funcs->io_write (channel,
-                                      channel->write_buf->str + bytes_written,
-                                      channel->write_buf->len - bytes_written,
-                                      &this_time, error);
+                                        channel->write_buf->str + bytes_written,
+                                        channel->write_buf->len - bytes_written,
+                                        &this_time, error);
       bytes_written += this_time;
     }
   while ((bytes_written < channel->write_buf->len)
@@ -863,6 +947,21 @@ g_io_channel_flush (GIOChannel     *channel,
  * The buffering state can only be set if the channel's encoding
  * is %NULL. For any other encoding, the channel must be buffered.
  *
+ * A buffered channel can only be set unbuffered if the channel's
+ * internal buffers have been flushed. Newly created channels or
+ * channels which have returned %G_IO_STATUS_EOF
+ * not require such a flush. For write-only channels, a call to
+ * g_io_channel_flush () is sufficient. For all other channels,
+ * the buffers may be flushed by a call to g_io_channel_seek_position ().
+ * This includes the possibility of seeking with seek type %G_SEEK_CUR
+ * and an offset of zero. Note that this means that socket-based
+ * channels cannot be set unbuffered once they have had data
+ * read from them.
+ *
+ * On unbuffered channels, it is safe to mix read and write
+ * calls from the new and old APIs, if this is necessary for
+ * maintaining old code.
+ *
  * The default state of the channel is buffered.
  **/
 void
@@ -886,9 +985,11 @@ g_io_channel_set_buffered  (GIOChannel *channel,
 
 /**
  * g_io_channel_get_buffered:
- * @channel: a #GIOChannel
+ * @channel: a #GIOChannel.
  *
- * Return Value: the buffering state of the channel
+ * Returns whether @channel is buffered.
+ *
+ * Return Value: %TRUE if the @channel is buffered. 
  **/
 gboolean
 g_io_channel_get_buffered      (GIOChannel *channel)
@@ -904,30 +1005,43 @@ g_io_channel_get_buffered        (GIOChannel *channel)
  * @encoding: the encoding type
  * @error: location to store an error of type #GConvertError.
  *
- * Set the encoding for the input/output of the channel. The internal
+ * Sets the encoding for the input/output of the channel. The internal
  * encoding is always UTF-8. The default encoding for the
  * external file is UTF-8.
  *
  * 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.
  *
- * The encoding can only be set under three conditions:
+ * The encoding can only be set if one of the following conditions
+ * is true:
  *
  * 1. The channel was just created, and has not been written to
  *    or read from yet.
  *
- * 2. The channel is a file, and the file pointer was just
+ * 2. The channel is write-only.
+ *
+ * 3. The channel is a file, and the file pointer was just
  *    repositioned by a call to g_io_channel_seek_position().
  *    (This flushes all the internal buffers.)
  *
- * 3. The current encoding is %NULL or UTF-8.
+ * 4. The current encoding is %NULL or UTF-8.
+ *
+ * 5. One of the (new API) read functions has just returned %G_IO_STATUS_EOF
+ *    (or, in the case of g_io_channel_read_to_end (), %G_IO_STATUS_NORMAL).
  *
- * Return Value: %G_IO_STATUS_NORMAL if the encoding was succesfully set.
+ * 6. One of the functions g_io_channel_read_chars () or g_io_channel_read_unichar ()
+ *    has returned %G_IO_STATUS_AGAIN or %G_IO_STATUS_ERROR. This may be
+ *    useful in the case of %G_CONVERT_ERROR_ILLEGAL_SEQUENCE.
+ *    Returning one of these statuses from g_io_channel_read_line (),
+ *    g_io_channel_read_line_string (), or g_io_channel_read_to_end ()
+ *    does <emphasis>not</emphasis> guarantee that the encoding can be changed.
+ *
+ * Channels which do not meet one of the above conditions cannot call
+ * g_io_channel_seek_position () with an offset of %G_SEEK_CUR,
+ * and, if they are "seekable", cannot
+ * call g_io_channel_write_chars () after calling one
+ * of the API "read" functions.
+ *
+ * Return Value: %G_IO_STATUS_NORMAL if the encoding was successfully set.
  **/
 GIOStatus
 g_io_channel_set_encoding (GIOChannel  *channel,
@@ -1011,7 +1125,7 @@ g_io_channel_set_encoding (GIOChannel     *channel,
           else
             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));
+                         from_enc, to_enc, g_strerror (err));
 
           if (read_cd != (GIConv) -1)
             g_iconv_close (read_cd);
@@ -1057,11 +1171,9 @@ g_io_channel_set_encoding (GIOChannel    *channel,
  * g_io_channel_get_encoding:
  * @channel: a #GIOChannel
  *
- * Get 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. Both
- * %G_IO_CHANNEL_ENCODE_RAW and the default (no encoding, but buffered)
- * are safe to use with binary data.
+ * Gets the encoding for the input/output of the channel. The internal
+ * encoding is always UTF-8. The encoding %NULL makes the
+ * channel safe for binary data.
  *
  * Return value: A string containing the encoding, this string is
  *   owned by GLib and must not be freed.
@@ -1126,24 +1238,32 @@ g_io_channel_fill_buffer (GIOChannel *channel,
     {
       size_t errnum, inbytes_left, outbytes_left;
       gchar *inbuf, *outbuf;
+      int errval;
 
       g_assert (channel->encoded_read_buf);
 
 reencode:
 
       inbytes_left = channel->read_buf->len;
-      outbytes_left = MAX (6, MAX (channel->read_buf->len,
+      outbytes_left = MAX (channel->read_buf->len,
                            channel->encoded_read_buf->allocated_len
-                           - channel->encoded_read_buf->len));
+                           - channel->encoded_read_buf->len - 1); /* 1 for NULL */
+      outbytes_left = MAX (outbytes_left, 6);
 
       inbuf = channel->read_buf->str;
-      outbuf = channel->encoded_read_buf->str + channel->encoded_read_buf->len;
-
       g_string_set_size (channel->encoded_read_buf,
                          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);
+      errval = errno;
+
+      g_assert (inbuf + inbytes_left == channel->read_buf->str
+                + channel->read_buf->len);
+      g_assert (outbuf + outbytes_left == channel->encoded_read_buf->str
+                + channel->encoded_read_buf->len);
 
       g_string_erase (channel->read_buf, 0,
                      channel->read_buf->len - inbytes_left);
@@ -1152,7 +1272,7 @@ reencode:
 
       if (errnum == (size_t) -1)
         {
-          switch (errno)
+          switch (errval)
             {
               case EINVAL:
                 if ((oldlen == channel->encoded_read_buf->len)
@@ -1177,8 +1297,9 @@ reencode:
                   }
                 break;
               default:
+                g_assert (errval != EBADF); /* The converter should be open */
                 g_set_error (err, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
-                  _("Error during conversion: %s"), strerror (errno));
+                  _("Error during conversion: %s"), g_strerror (errval));
                 return G_IO_STATUS_ERROR;
             }
         }
@@ -1207,7 +1328,7 @@ reencode:
                 lastchar = nextchar;
                 break;
               case -1:
-                if (oldlen > channel->encoded_read_buf->len)
+                if (oldlen < channel->encoded_read_buf->len)
                   status = G_IO_STATUS_NORMAL;
                 else
                   {
@@ -1240,23 +1361,21 @@ reencode:
 /**
  * g_io_channel_read_line:
  * @channel: a #GIOChannel
- * @str_return: The line read from the #GIOChannel, not including the
+ * @str_return: The line read from the #GIOChannel, including the
  *              line terminator. This data should be freed with g_free()
- *              when no longer needed. This
- *              is a null terminated string. If a @length of zero is
- *              returned, this will be %NULL instead.
+ *              when no longer needed. This is a nul-terminated string. 
+ *              If a @length of zero is returned, this will be %NULL instead.
  * @length: location to store length of the read data, or %NULL
  * @terminator_pos: location to store position of line terminator, or %NULL
  * @error: A location to return an error of type #GConvertError
  *         or #GIOChannelError
  *
- * Read a line, not including the terminating character(s),
- * from a #GIOChannel into a newly allocated string.
- * @length will contain allocated memory if the return
+ * Reads a line, including the terminating character(s),
+ * from a #GIOChannel into a newly-allocated string.
+ * @str_return will contain allocated memory if the return
  * is %G_IO_STATUS_NORMAL.
  *
- * Return value: a newly allocated string. Free this string
- *   with g_free() when you are done with it.
+ * Return value: the status of the operation.
  **/
 GIOStatus
 g_io_channel_read_line (GIOChannel *channel,
@@ -1295,15 +1414,15 @@ g_io_channel_read_line (GIOChannel *channel,
  * g_io_channel_read_line_string:
  * @channel: a #GIOChannel
  * @buffer: a #GString into which the line will be written.
- *          If @buffer already contains data, the new data will
- *          be appended to it.
+ *          If @buffer already contains data, the old data will
+ *          be overwritten.
  * @terminator_pos: location to store position of line terminator, or %NULL
  * @error: a location to store an error of type #GConvertError
  *         or #GIOChannelError
  *
- * Read a line from a #GIOChannel, using a #GString as a buffer.
+ * Reads a line from a #GIOChannel, using a #GString as a buffer.
  *
- * Return value:
+ * Return value: the status of the operation.
  **/
 GIOStatus
 g_io_channel_read_line_string (GIOChannel *channel,
@@ -1357,7 +1476,7 @@ g_io_channel_read_line_backend    (GIOChannel *channel,
   status = G_IO_STATUS_NORMAL;
 
   if (channel->line_term)
-    line_term_len = strlen (channel->line_term);
+    line_term_len = channel->line_term_len;
   else
     line_term_len = 3;
     /* This value used for setting checked_to, it's the longest of the four
@@ -1397,7 +1516,7 @@ read_again:
                       {
                         g_set_error (error, G_CONVERT_ERROR,
                                      G_CONVERT_ERROR_PARTIAL_INPUT,
-                                     "Leftover unconverted data in read buffer");
+                                     _("Leftover unconverted data in read buffer"));
                         return G_IO_STATUS_ERROR;
                       }
                     else
@@ -1417,14 +1536,14 @@ read_again:
 
       first_time = FALSE;
 
-      lastchar = use_buf->str + strlen (use_buf->str);
+      lastchar = use_buf->str + use_buf->len;
 
       for (nextchar = use_buf->str + checked_to; nextchar < lastchar;
            channel->encoding ? nextchar = g_utf8_next_char (nextchar) : nextchar++)
         {
           if (channel->line_term)
             {
-              if (strncmp (channel->line_term, nextchar, line_term_len) == 0)
+              if (memcmp (channel->line_term, nextchar, line_term_len) == 0)
                 {
                   line_length = nextchar - use_buf->str;
                   got_term_len = line_term_len;
@@ -1457,22 +1576,18 @@ read_again:
                         goto done;
                       }
                     break;
+                  case '\0': /* Embeded null in input */
+                    line_length = nextchar - use_buf->str;
+                    got_term_len = 1;
+                    goto done;
                   default: /* no match */
                     break;
                 }
             }
         }
 
-      g_assert (nextchar == lastchar); /* Valid UTF-8, didn't overshoot */
-
-      /* Also terminate on '\0' */
-
-      line_length = lastchar - use_buf->str;
-      if (line_length < use_buf->len)
-        {
-          got_term_len = 0;
-          break;
-        }
+      /* If encoding != NULL, valid UTF-8, didn't overshoot */
+      g_assert (nextchar == lastchar);
 
       /* Check for EOF */
 
@@ -1481,7 +1596,7 @@ read_again:
           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");
+                           _("Channel terminates in a partial character"));
               return G_IO_STATUS_ERROR;
             }
           line_length = use_buf->len;
@@ -1512,17 +1627,16 @@ done:
  * @str_return: Location to store a pointer to a string holding
  *              the remaining data in the #GIOChannel. This data should
  *              be freed with g_free() when no longer needed. This
- *              data is terminated by an extra null, but there may be other
- *              nulls in the intervening data.
+ *              data is terminated by an extra nul character, but there 
+ *              may be other nuls in the intervening data.
  * @length: Location to store length of the data
  * @error: A location to return an error of type #GConvertError
  *         or #GIOChannelError
  *
- * Read all the remaining data from the file. Parameters as
- * for g_io_channel_read_line.
+ * Reads all the remaining data from the file.
  *
- * Return value: One of #G_IO_STATUS_EOF or #G_IO_STATUS_PARTIAL_CHARS
- *               on success
+ * Return value: %G_IO_STATUS_NORMAL on success. This function never
+ *               returns %G_IO_STATUS_EOF.
  **/
 GIOStatus
 g_io_channel_read_to_end (GIOChannel   *channel,
@@ -1559,7 +1673,7 @@ g_io_channel_read_to_end (GIOChannel      *channel,
   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");
+                   _("Channel terminates in a partial character"));
       return G_IO_STATUS_ERROR;
     }
 
@@ -1579,8 +1693,10 @@ g_io_channel_read_to_end (GIOChannel     *channel,
       else
         g_string_free (USE_BUF (channel), TRUE);
 
-      /* This only works because USE_BUF () is a macro */
-      USE_BUF (channel) = NULL;
+      if (channel->encoding)
+       channel->encoded_read_buf = NULL;
+      else
+       channel->read_buf = NULL;
     }
 
   return G_IO_STATUS_NORMAL;
@@ -1594,13 +1710,16 @@ g_io_channel_read_to_end (GIOChannel    *channel,
  *         not be complelely filled even if there is data
  *         in the buffer if the remaining data is not a
  *         complete character.
- * @bytes_read: The number of bytes read.
+ * @bytes_read: The number of bytes read. This may be zero even on
+ *              success if count < 6 and the channel's encoding is non-%NULL.
+ *              This indicates that the next UTF-8 character is too wide for
+ *              the buffer.
  * @error: A location to return an error of type #GConvertError
  *         or #GIOChannelError.
  *
  * Replacement for g_io_channel_read() with the new API.
  *
- * Return value:
+ * Return value: the status of the operation.
  **/
 GIOStatus
 g_io_channel_read_chars (GIOChannel    *channel,
@@ -1626,9 +1745,16 @@ g_io_channel_read_chars (GIOChannel      *channel,
 
   if (!channel->use_buffer)
     {
+      gsize tmp_bytes;
+      
       g_assert (!channel->read_buf || channel->read_buf->len == 0);
 
-      return channel->funcs->io_read (channel, buf, count, bytes_read, error);
+      status = channel->funcs->io_read (channel, buf, count, &tmp_bytes, error);
+      
+      if (bytes_read)
+       *bytes_read = tmp_bytes;
+
+      return status;
     }
 
   status = G_IO_STATUS_NORMAL;
@@ -1647,7 +1773,7 @@ g_io_channel_read_chars (GIOChannel       *channel,
         {
           g_set_error (error, G_CONVERT_ERROR,
                        G_CONVERT_ERROR_PARTIAL_INPUT,
-                       "Leftover unconverted data in read buffer");
+                       _("Leftover unconverted data in read buffer"));
           status = G_IO_STATUS_ERROR;
         }
 
@@ -1677,6 +1803,7 @@ g_io_channel_read_chars (GIOChannel       *channel,
         {
           prevchar = nextchar;
           nextchar = g_utf8_next_char (nextchar);
+          g_assert (nextchar != prevchar); /* Possible for *prevchar of -1 or -2 */
         }
       while (nextchar < channel->encoded_read_buf->str + got_bytes);
 
@@ -1732,7 +1859,7 @@ g_io_channel_read_unichar     (GIOChannel   *channel,
         {
           g_set_error (error, G_CONVERT_ERROR,
                        G_CONVERT_ERROR_PARTIAL_INPUT,
-                       "Leftover unconverted data in read buffer");
+                       _("Leftover unconverted data in read buffer"));
           status = G_IO_STATUS_ERROR;
         }
 
@@ -1760,15 +1887,23 @@ g_io_channel_read_unichar     (GIOChannel   *channel,
  * @channel: a #GIOChannel
  * @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.
+ *         is taken to be a nul-terminated string.
  * @bytes_written: The number of bytes written. This can be nonzero
  *                 even if the return value is not %G_IO_STATUS_NORMAL.
+ *                 If the return value is %G_IO_STATUS_NORMAL and the
+ *                 channel is blocking, this will always be equal
+ *                 to @count if @count >= 0.
  * @error: A location to return an error of type #GConvertError
  *         or #GIOChannelError
  *
  * Replacement for g_io_channel_write() with the new API.
  *
- * Return value:
+ * On seekable channels with encodings other than %NULL or UTF-8, generic
+ * mixing of reading and writing is not allowed. A call to g_io_channel_write_chars ()
+ * may only be made on a channel from which data has been read in the
+ * cases described in the documentation for g_io_channel_set_encoding ().
+ *
+ * Return value: the status of the operation.
  **/
 GIOStatus
 g_io_channel_write_chars (GIOChannel   *channel,
@@ -1789,7 +1924,11 @@ g_io_channel_write_chars (GIOChannel     *channel,
     count = strlen (buf);
   
   if (count == 0)
-    return G_IO_STATUS_NORMAL;
+    {
+      if (bytes_written)
+        *bytes_written = 0;
+      return G_IO_STATUS_NORMAL;
+    }
 
   g_return_val_if_fail (buf != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail (count > 0, G_IO_STATUS_ERROR);
@@ -1798,9 +1937,17 @@ g_io_channel_write_chars (GIOChannel     *channel,
 
   if (!channel->use_buffer)
     {
+      gsize tmp_bytes;
+      
       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);
+      
+      status = channel->funcs->io_write (channel, buf, count, &tmp_bytes, error);
+
+      if (bytes_written)
+       *bytes_written = tmp_bytes;
+
+      return status;
     }
 
   /* General case */
@@ -1815,7 +1962,11 @@ g_io_channel_write_chars (GIOChannel     *channel,
         }
       status = g_io_channel_seek_position (channel, 0, G_SEEK_CUR, error);
       if (status != G_IO_STATUS_NORMAL)
-        return status;
+        {
+          if (bytes_written)
+            *bytes_written = 0;
+          return status;
+        }
     }
 
   if (!channel->write_buf)
@@ -1857,8 +2008,8 @@ g_io_channel_write_chars (GIOChannel      *channel,
             }
         }
 
-      space_in_buf = MAX (channel->buf_size, channel->write_buf->allocated_len)
-                     - channel->write_buf->len;
+      space_in_buf = MAX (channel->buf_size, channel->write_buf->allocated_len - 1)
+                     - channel->write_buf->len; /* 1 for NULL */
 
       /* This is only true because g_io_channel_set_buffer_size ()
        * ensures that channel->buf_size >= MAX_CHAR_SIZE.
@@ -1901,40 +2052,54 @@ g_io_channel_write_chars (GIOChannel    *channel,
 
 reconvert:
 
-          if (!channel->do_encode)
+          if (!channel->do_encode) /* UTF-8 encoding */
             {
               const gchar *badchar;
+              gsize try_len = MIN (from_buf_len, space_in_buf);
 
               /* UTF-8, just validate, emulate g_iconv */
 
-              if (!g_utf8_validate (from_buf, from_buf_len, &badchar))
+              if (!g_utf8_validate (from_buf, try_len, &badchar))
                 {
                   gunichar try_char;
+                  gsize incomplete_len = from_buf + try_len - badchar;
 
                   left_len = from_buf + from_buf_len - badchar;
 
-                  try_char = g_utf8_get_char_validated (badchar, left_len);
+                  try_char = g_utf8_get_char_validated (badchar, incomplete_len);
 
                   switch (try_char)
                     {
                       case -2:
-                        g_assert (left_len < 6);
-                        errnum = EINVAL;
+                        g_assert (incomplete_len < 6);
+                        if (try_len == from_buf_len)
+                          {
+                            errnum = EINVAL;
+                            err = (size_t) -1;
+                          }
+                        else
+                          {
+                            errnum = 0;
+                            err = (size_t) 0;
+                          }
                         break;
                       case -1:
+                        g_warning ("Invalid UTF-8 passed to g_io_channel_write_chars().");
+                        /* FIXME bail here? */
                         errnum = EILSEQ;
+                        err = (size_t) -1;
                         break;
                       default:
                         g_assert_not_reached ();
+                        err = (size_t) -1;
                         errnum = 0; /* Don't confunse the compiler */
                     }
-                  err = (size_t) -1;
                 }
               else
                 {
                   err = (size_t) 0;
                   errnum = 0;
-                  left_len = 0;
+                  left_len = from_buf_len - try_len;
                 }
 
               g_string_append_len (channel->write_buf, from_buf,
@@ -1946,9 +2111,10 @@ reconvert:
                gchar *outbuf;
 
                left_len = from_buf_len;
-               outbuf = channel->write_buf->str + channel->write_buf->len;
                g_string_set_size (channel->write_buf, channel->write_buf->len
                                   + space_in_buf);
+               outbuf = channel->write_buf->str + channel->write_buf->len
+                        - space_in_buf;
                err = g_iconv (channel->write_cd, (gchar **) &from_buf, &left_len,
                               &outbuf, &space_in_buf);
                errnum = errno;
@@ -1969,6 +2135,8 @@ reconvert:
 
                         memcpy (channel->partial_write_buf, from_buf, left_len);
                         channel->partial_write_buf[left_len] = '\0';
+                        if (bytes_written)
+                          *bytes_written = count;
                         return G_IO_STATUS_NORMAL;
                       }
 
@@ -2020,7 +2188,7 @@ reconvert:
                     return G_IO_STATUS_ERROR;
                   default:
                     g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
-                      _("Error during conversion: %s"), strerror (errno));
+                      _("Error during conversion: %s"), g_strerror (errnum));
                     if (from_buf_len >= left_len + from_buf_old_len)
                       wrote_bytes += from_buf_len - left_len - from_buf_old_len;
                     if (bytes_written)
@@ -2047,7 +2215,7 @@ reconvert:
     }
 
   if (bytes_written)
-    *bytes_written = wrote_bytes;
+    *bytes_written = count;
 
   return G_IO_STATUS_NORMAL;
 }