Index: glib/gconvert.c
===================================================================
RCS file: /cvs/gnome/glib/glib/gconvert.c,v
retrieving revision 1.74
diff -u -p -r1.74 gconvert.c
--- glib/gconvert.c	27 Apr 2005 09:50:08 -0000	1.74
+++ glib/gconvert.c	2 Aug 2005 06:29:09 -0000
@@ -463,109 +463,13 @@ close_converter (GIConv converter)
   return 0;
 }
 
-
-/**
- * g_convert:
- * @str:           the string to convert
- * @len:           the length of the string, or -1 if the string is 
- *                 nul-terminated<footnote id="nul-unsafe">
-                     <para>
-                       Note that some encodings may allow nul bytes to 
-                       occur inside strings. In that case, using -1 for 
-                       the @len parameter is unsafe.
-                     </para>
-                   </footnote>. 
- * @to_codeset:    name of character set into which to convert @str
- * @from_codeset:  character set of @str.
- * @bytes_read:    location to store the number of bytes in the
- *                 input string that were successfully converted, or %NULL.
- *                 Even if the conversion was successful, this may be 
- *                 less than @len if there were partial characters
- *                 at the end of the input. If the error
- *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- *                 stored will the byte offset after the last valid
- *                 input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not 
- *                 including the terminating nul).
- * @error:         location to store the error occuring, or %NULL to ignore
- *                 errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from one character set to another.
- *
- * Return value: If the conversion was successful, a newly allocated
- *               nul-terminated string, which must be freed with
- *               g_free(). Otherwise %NULL and @error will be set.
- **/
-gchar*
-g_convert (const gchar *str,
-           gssize       len,  
-           const gchar *to_codeset,
-           const gchar *from_codeset,
-           gsize       *bytes_read, 
-	   gsize       *bytes_written, 
-	   GError     **error)
-{
-  gchar *res;
-  GIConv cd;
-  
-  g_return_val_if_fail (str != NULL, NULL);
-  g_return_val_if_fail (to_codeset != NULL, NULL);
-  g_return_val_if_fail (from_codeset != NULL, NULL);
-  
-  cd = open_converter (to_codeset, from_codeset, error);
-
-  if (cd == (GIConv) -1)
-    {
-      if (bytes_read)
-        *bytes_read = 0;
-      
-      if (bytes_written)
-        *bytes_written = 0;
-      
-      return NULL;
-    }
-
-  res = g_convert_with_iconv (str, len, cd,
-			      bytes_read, bytes_written,
-			      error);
-  
-  close_converter (cd);
-
-  return res;
-}
-
-/**
- * g_convert_with_iconv:
- * @str:           the string to convert
- * @len:           the length of the string, or -1 if the string is 
- *                 nul-terminated<footnoteref linkend="nul-unsafe"/>. 
- * @converter:     conversion descriptor from g_iconv_open()
- * @bytes_read:    location to store the number of bytes in the
- *                 input string that were successfully converted, or %NULL.
- *                 Even if the conversion was successful, this may be 
- *                 less than @len if there were partial characters
- *                 at the end of the input. If the error
- *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
- *                 stored will the byte offset after the last valid
- *                 input sequence.
- * @bytes_written: the number of bytes stored in the output buffer (not 
- *                 including the terminating nul).
- * @error:         location to store the error occuring, or %NULL to ignore
- *                 errors. Any of the errors in #GConvertError may occur.
- *
- * Converts a string from one character set to another.
- *
- * Return value: If the conversion was successful, a newly allocated
- *               nul-terminated string, which must be freed with
- *               g_free(). Otherwise %NULL and @error will be set.
- **/
-gchar*
-g_convert_with_iconv (const gchar *str,
-		      gssize       len,
-		      GIConv       converter,
-		      gsize       *bytes_read, 
-		      gsize       *bytes_written, 
-		      GError     **error)
+static gchar*
+g_convert_with_iconv_internal (const gchar *str,
+			       gssize       len,
+			       GIConv       converter,
+			       gsize       *bytes_read, 
+			       gsize       *bytes_written, 
+			       GError     **error)
 {
   gchar *dest;
   gchar *outp;
@@ -576,9 +480,6 @@ g_convert_with_iconv (const gchar *str,
   gsize outbuf_size;
   gboolean have_error = FALSE;
   
-  g_return_val_if_fail (str != NULL, NULL);
-  g_return_val_if_fail (converter != (GIConv) -1, NULL);
-     
   if (len < 0)
     len = strlen (str);
 
@@ -658,6 +559,153 @@ g_convert_with_iconv (const gchar *str,
     return dest;
 }
 
+
+/**
+ * g_convert_with_iconv:
+ * @str:           the string to convert
+ * @len:           the length of the string, or -1 if the string is 
+ *                 nul-terminated<footnoteref linkend="nul-unsafe"/>. 
+ * @converter:     conversion descriptor from g_iconv_open()
+ * @bytes_read:    location to store the number of bytes in the
+ *                 input string that were successfully converted, or %NULL.
+ *                 Even if the conversion was successful, this may be 
+ *                 less than @len if there were partial characters
+ *                 at the end of the input. If the error
+ *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ *                 stored will the byte offset after the last valid
+ *                 input sequence.
+ * @bytes_written: the number of bytes stored in the output buffer (not 
+ *                 including the terminating nul).
+ * @error:         location to store the error occuring, or %NULL to ignore
+ *                 errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from one character set to another.
+ *
+ * Note that this function does not attempt to clean up the @converter
+ * state, so that it can be used repeatedly to convert a large sequence
+ * in chunks. The caller is responsible for handling a possible pending 
+ * final shift sequence by calling 
+ * <literal>g_iconv (converter, NULL, NULL, outbuf, outbytes_left)</literal>
+ * before closing @converter. 
+ * The functions g_convert() and g_convert_with_fallback() handle this 
+ * automatically. 
+ *
+ * Return value: If the conversion was successful, a newly allocated
+ *               nul-terminated string, which must be freed with
+ *               g_free(). Otherwise %NULL and @error will be set.
+ **/
+gchar*
+g_convert_with_iconv (const gchar *str,
+		      gssize       len,
+		      GIConv       converter,
+		      gsize       *bytes_read, 
+		      gsize       *bytes_written, 
+		      GError     **error)
+{
+  g_return_val_if_fail (str != NULL, NULL);
+  g_return_val_if_fail (converter != (GIConv) -1, NULL);
+     
+  return g_convert_with_iconv_internal (str, len, converter, 
+					bytes_read, bytes_written, 
+					error);
+}
+
+/**
+ * g_convert:
+ * @str:           the string to convert
+ * @len:           the length of the string, or -1 if the string is 
+ *                 nul-terminated<footnote id="nul-unsafe">
+                     <para>
+                       Note that some encodings may allow nul bytes to 
+                       occur inside strings. In that case, using -1 for 
+                       the @len parameter is unsafe.
+                     </para>
+                   </footnote>. 
+ * @to_codeset:    name of character set into which to convert @str
+ * @from_codeset:  character set of @str.
+ * @bytes_read:    location to store the number of bytes in the
+ *                 input string that were successfully converted, or %NULL.
+ *                 Even if the conversion was successful, this may be 
+ *                 less than @len if there were partial characters
+ *                 at the end of the input. If the error
+ *                 #G_CONVERT_ERROR_ILLEGAL_SEQUENCE occurs, the value
+ *                 stored will the byte offset after the last valid
+ *                 input sequence.
+ * @bytes_written: the number of bytes stored in the output buffer (not 
+ *                 including the terminating nul).
+ * @error:         location to store the error occuring, or %NULL to ignore
+ *                 errors. Any of the errors in #GConvertError may occur.
+ *
+ * Converts a string from one character set to another.
+ *
+ * Return value: If the conversion was successful, a newly allocated
+ *               nul-terminated string, which must be freed with
+ *               g_free(). Otherwise %NULL and @error will be set.
+ **/
+gchar*
+g_convert (const gchar *str,
+           gssize       len,  
+           const gchar *to_codeset,
+           const gchar *from_codeset,
+           gsize       *bytes_read, 
+	   gsize       *bytes_written, 
+	   GError     **error)
+{
+  gchar *res;
+  GIConv cd;
+  GError *local_error = NULL;
+  gchar *shift, *tmp;
+  gsize shift_bytes_read;
+  gsize shift_bytes_written;
+
+  g_return_val_if_fail (str != NULL, NULL);
+  g_return_val_if_fail (to_codeset != NULL, NULL);
+  g_return_val_if_fail (from_codeset != NULL, NULL);
+  
+  cd = open_converter (to_codeset, from_codeset, error);
+
+  if (cd == (GIConv) -1)
+    {
+      if (bytes_read)
+        *bytes_read = 0;
+      
+      if (bytes_written)
+        *bytes_written = 0;
+      
+      return NULL;
+    }
+
+  res = g_convert_with_iconv_internal (str, len, cd,
+				       bytes_read, bytes_written,
+				       error);
+  if (res)
+    {
+      shift_bytes_read = shift_bytes_written = 0;
+      shift = g_convert_with_iconv_internal (NULL, 0, cd,
+					     &shift_bytes_read, 
+					     &shift_bytes_written, 
+					     &local_error);
+
+      if (local_error)
+	{
+	  g_propagate_error (error, local_error);
+	}
+      else if (shift_bytes_written > 0)
+	{
+	  if (bytes_written)
+	    *bytes_written += shift_bytes_written;
+	  
+	  tmp = g_strconcat (res, shift, NULL);
+	  g_free (res);
+	  res = tmp;
+	}
+    }
+
+  close_converter (cd);
+
+  return res;
+}
+
 /**
  * g_convert_with_fallback:
  * @str:          the string to convert
@@ -861,6 +909,43 @@ g_convert_with_fallback (const gchar *st
 
   /* Cleanup
    */
+
+  if (!have_error)
+    {
+      GError *local_error = NULL;
+      gchar *shift;
+      gsize shift_bytes_read;
+      gsize shift_bytes_written;
+
+      shift_bytes_read = shift_bytes_written = 0;
+      shift = g_convert_with_iconv_internal (NULL, 0, cd,
+					     &shift_bytes_read, 
+					     &shift_bytes_written, 
+					     &local_error);
+      
+      if (local_error)
+	{
+	  g_propagate_error (error, local_error);
+	  have_error = TRUE;
+	}
+      else
+	{
+	  if (shift_bytes_written > outbytes_remaining)
+	    {
+	      gsize used = outp - dest;
+	      
+	      outbuf_size += shift_bytes_written - outbytes_remaining;
+	      dest = g_realloc (dest, outbuf_size);
+	      
+	      outp = dest + used;
+	      outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */    
+	    }
+
+	  memcpy (outp, shift, shift_bytes_written);
+	  outp += shift_bytes_written;
+	}
+    }
+  
   *outp = '\0';
   
   close_converter (cd);
