[kdbus] Add initial support for receiving messages
[platform/upstream/glib.git] / gio / gcharsetconverter.c
index c2f2c7d..86be20e 100644 (file)
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: Alexander Larsson <alexl@redhat.com>
  */
 
 #include "config.h"
 
+#include "gcharsetconverter.h"
+
 #include <errno.h>
 
-#include "gcontenttypeprivate.h"
-#include "gcharsetconverter.h"
-#include "glib.h"
 #include "ginitable.h"
 #include "gioerror.h"
 #include "glibintl.h"
 
-#include "gioalias.h"
 
 enum {
   PROP_0,
   PROP_FROM_CHARSET,
-  PROP_TO_CHARSET
+  PROP_TO_CHARSET,
+  PROP_USE_FALLBACK
 };
 
 /**
@@ -63,6 +60,8 @@ struct _GCharsetConverter
   char *from;
   char *to;
   GIConv iconv;
+  gboolean use_fallback;
+  guint n_fallback_errors;
 };
 
 G_DEFINE_TYPE_WITH_CODE (GCharsetConverter, g_charset_converter, G_TYPE_OBJECT,
@@ -108,6 +107,10 @@ g_charset_converter_set_property (GObject      *object,
       conv->from = g_value_dup_string (value);
       break;
 
+    case PROP_USE_FALLBACK:
+      conv->use_fallback = g_value_get_boolean (value);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -135,6 +138,10 @@ g_charset_converter_get_property (GObject    *object,
       g_value_set_string (value, conv->from);
       break;
 
+    case PROP_USE_FALLBACK:
+      g_value_set_boolean (value, conv->use_fallback);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -166,7 +173,15 @@ g_charset_converter_class_init (GCharsetConverterClass *klass)
                                                        NULL,
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                                                        G_PARAM_STATIC_STRINGS));
-
+  g_object_class_install_property (gobject_class,
+                                  PROP_USE_FALLBACK,
+                                  g_param_spec_boolean ("use-fallback",
+                                                        P_("Fallback enabled"),
+                                                        P_("Use fallback (of form \\<hexval>) for invalid bytes"),
+                                                        FALSE,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -188,9 +203,9 @@ g_charset_converter_init (GCharsetConverter *local)
  * Since: 2.24
  **/
 GCharsetConverter *
-g_charset_converter_new (const gchar  *to_charset,
-                        const gchar  *from_charset,
-                        GError       **error)
+g_charset_converter_new (const gchar *to_charset,
+                        const gchar *from_charset,
+                        GError      **error)
 {
   GCharsetConverter *conv;
 
@@ -215,18 +230,19 @@ g_charset_converter_reset (GConverter *converter)
     }
 
   g_iconv (conv->iconv, NULL, NULL, NULL, NULL);
+  conv->n_fallback_errors = 0;
 }
 
 static GConverterResult
-g_charset_converter_convert (GConverter *converter,
-                            const void *inbuf,
-                            gsize       inbuf_size,
-                            void       *outbuf,
-                            gsize       outbuf_size,
-                            GConverterFlags flags,
-                            gsize      *bytes_read,
-                            gsize      *bytes_written,
-                            GError    **error)
+g_charset_converter_convert (GConverter       *converter,
+                            const void       *inbuf,
+                            gsize             inbuf_size,
+                            void             *outbuf,
+                            gsize             outbuf_size,
+                            GConverterFlags   flags,
+                            gsize            *bytes_read,
+                            gsize            *bytes_written,
+                            GError          **error)
 {
   GCharsetConverter  *conv;
   gsize res;
@@ -234,6 +250,7 @@ g_charset_converter_convert (GConverter *converter,
   gchar *inbufp, *outbufp;
   gsize in_left, out_left;
   int errsv;
+  gboolean reset;
 
   conv = G_CHARSET_CONVERTER (converter);
 
@@ -244,29 +261,37 @@ g_charset_converter_convert (GConverter *converter,
       return G_CONVERTER_ERROR;
     }
 
-  /* Iconv never produces output with no input, so handle this
-     specially */
-  if (inbuf_size == 0)
-    {
-      if (flags & G_CONVERTER_INPUT_AT_END)
-       return G_CONVERTER_FINISHED;
-
-      if (flags & G_CONVERTER_FLUSH)
-       return G_CONVERTER_FLUSHED;
-
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
-                          _("Incomplete multibyte sequence in input"));
-      return G_CONVERTER_ERROR;
-    }
-
   inbufp = (char *)inbuf;
   outbufp = (char *)outbuf;
   in_left = inbuf_size;
   out_left = outbuf_size;
+  reset = FALSE;
 
-  res = g_iconv (conv->iconv,
-                &inbufp, &in_left,
-                &outbufp, &out_left);
+  /* if there is not input try to flush the data */
+  if (inbuf_size == 0)
+    {
+      if (flags & G_CONVERTER_INPUT_AT_END ||
+          flags & G_CONVERTER_FLUSH)
+        {
+          reset = TRUE;
+        }
+      else
+        {
+          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
+                               _("Incomplete multibyte sequence in input"));
+          return G_CONVERTER_ERROR;
+        }
+    }
+
+  if (reset)
+    /* call g_iconv with NULL inbuf to cleanup shift state */
+    res = g_iconv (conv->iconv,
+                   NULL, &in_left,
+                   &outbufp, &out_left);
+  else
+    res = g_iconv (conv->iconv,
+                   &inbufp, &in_left,
+                   &outbufp, &out_left);
 
   *bytes_read = inbufp - (char *)inbuf;
   *bytes_written = outbufp - (char *)outbuf;
@@ -292,8 +317,29 @@ g_charset_converter_convert (GConverter *converter,
 
        case EILSEQ:
          /* Invalid code sequence */
-         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
-                              _("Invalid byte sequence in conversion input"));
+         if (conv->use_fallback)
+           {
+             if (outbuf_size < 3)
+               g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+                                    _("Not enough space in destination"));
+             else
+               {
+                 const char hex[] = "0123456789ABCDEF";
+                 guint8 v = *(guint8 *)inbuf;
+                 guint8 *out = (guint8 *)outbuf;
+                 out[0] = '\\';
+                 out[1] = hex[(v & 0xf0) >> 4];
+                 out[2] = hex[(v & 0x0f) >> 0];
+                 *bytes_read = 1;
+                 *bytes_written = 3;
+                 in_left--;
+                 conv->n_fallback_errors++;
+                 goto ok;
+               }
+           }
+         else
+           g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                                _("Invalid byte sequence in conversion input"));
          break;
 
        default:
@@ -306,19 +352,74 @@ g_charset_converter_convert (GConverter *converter,
     }
   else
     {
+    ok:
       ret = G_CONVERTER_CONVERTED;
 
-      if (in_left == 0 &&
+      if (reset &&
          (flags & G_CONVERTER_INPUT_AT_END))
-       ret = G_CONVERTER_FINISHED;
-      else if (in_left == 0 &&
+        ret = G_CONVERTER_FINISHED;
+      else if (reset &&
               (flags & G_CONVERTER_FLUSH))
-       ret = G_CONVERTER_FLUSHED;
+        ret = G_CONVERTER_FLUSHED;
     }
 
   return ret;
 }
 
+/**
+ * g_charset_converter_set_use_fallback:
+ * @converter: a #GCharsetConverter
+ * @use_fallback: %TRUE to use fallbacks
+ *
+ * Sets the #GCharsetConverter:use-fallback property.
+ *
+ * Since: 2.24
+ */
+void
+g_charset_converter_set_use_fallback (GCharsetConverter *converter,
+                                     gboolean           use_fallback)
+{
+  use_fallback = !!use_fallback;
+
+  if (converter->use_fallback != use_fallback)
+    {
+      converter->use_fallback = use_fallback;
+      g_object_notify (G_OBJECT (converter), "use-fallback");
+    }
+}
+
+/**
+ * g_charset_converter_get_use_fallback:
+ * @converter: a #GCharsetConverter
+ *
+ * Gets the #GCharsetConverter:use-fallback property.
+ *
+ * Returns: %TRUE if fallbacks are used by @converter
+ *
+ * Since: 2.24
+ */
+gboolean
+g_charset_converter_get_use_fallback (GCharsetConverter *converter)
+{
+  return converter->use_fallback;
+}
+
+/**
+ * g_charset_converter_get_num_fallbacks:
+ * @converter: a #GCharsetConverter
+ *
+ * Gets the number of fallbacks that @converter has applied so far.
+ *
+ * Returns: the number of fallbacks that @converter has applied
+ *
+ * Since: 2.24
+ */
+guint
+g_charset_converter_get_num_fallbacks (GCharsetConverter *converter)
+{
+  return converter->n_fallback_errors;
+}
+
 static void
 g_charset_converter_iface_init (GConverterIface *iface)
 {
@@ -327,9 +428,9 @@ g_charset_converter_iface_init (GConverterIface *iface)
 }
 
 static gboolean
-g_charset_converter_initable_init (GInitable *initable,
-                                  GCancellable *cancellable,
-                                  GError  **error)
+g_charset_converter_initable_init (GInitable     *initable,
+                                  GCancellable  *cancellable,
+                                  GError       **error)
 {
   GCharsetConverter  *conv;
 
@@ -344,10 +445,9 @@ g_charset_converter_initable_init (GInitable *initable,
       return FALSE;
     }
 
-  conv->iconv =
-    g_iconv_open (conv->to, conv->from);
+  conv->iconv = g_iconv_open (conv->to, conv->from);
 
-  if (conv->iconv == NULL)
+  if (conv->iconv == (GIConv)-1)
     {
       if (errno == EINVAL)
        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
@@ -368,6 +468,3 @@ g_charset_converter_initable_iface_init (GInitableIface *iface)
 {
   iface->init = g_charset_converter_initable_init;
 }
-
-#define __G_CHARSET_CONVERTER_C__
-#include "gioaliasdef.c"