diff --git a/gio/Makefile.am b/gio/Makefile.am index d0b6935ed..446b2a4d5 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -187,6 +187,7 @@ libgio_2_0_la_SOURCES = \ gcancellable.c \ gcontenttype.c \ gcontenttypeprivate.h \ + gcharsetconverter.c \ gconverter.c \ gdatainputstream.c \ gdataoutputstream.c \ @@ -325,6 +326,7 @@ gio_headers = \ gbufferedoutputstream.h \ gcancellable.h \ gcontenttype.h \ + gcharsetconverter.h \ gconverter.h \ gdatainputstream.h \ gdataoutputstream.h \ diff --git a/gio/gcharsetconverter.c b/gio/gcharsetconverter.c new file mode 100644 index 000000000..c2f2c7db7 --- /dev/null +++ b/gio/gcharsetconverter.c @@ -0,0 +1,373 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: Alexander Larsson + */ + +#include "config.h" + +#include + +#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 +}; + +/** + * SECTION:gcharsetconverter + * @short_description: Convert between charsets + * @include: gio/gio.h + * + * #GCharsetConverter is an implementation of #GConverter based on + * GIConv. + */ + +static void g_charset_converter_iface_init (GConverterIface *iface); +static void g_charset_converter_initable_iface_init (GInitableIface *iface); + +/** + * GCharsetConverter: + * + * Conversions between character sets. + */ +struct _GCharsetConverter +{ + GObject parent_instance; + + char *from; + char *to; + GIConv iconv; +}; + +G_DEFINE_TYPE_WITH_CODE (GCharsetConverter, g_charset_converter, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, + g_charset_converter_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_charset_converter_initable_iface_init)) + +static void +g_charset_converter_finalize (GObject *object) +{ + GCharsetConverter *conv; + + conv = G_CHARSET_CONVERTER (object); + + g_free (conv->from); + g_free (conv->to); + if (conv->iconv) + g_iconv_close (conv->iconv); + + G_OBJECT_CLASS (g_charset_converter_parent_class)->finalize (object); +} + +static void +g_charset_converter_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GCharsetConverter *conv; + + conv = G_CHARSET_CONVERTER (object); + + switch (prop_id) + { + case PROP_TO_CHARSET: + g_free (conv->to); + conv->to = g_value_dup_string (value); + break; + + case PROP_FROM_CHARSET: + g_free (conv->from); + conv->from = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + +} + +static void +g_charset_converter_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GCharsetConverter *conv; + + conv = G_CHARSET_CONVERTER (object); + + switch (prop_id) + { + case PROP_TO_CHARSET: + g_value_set_string (value, conv->to); + break; + + case PROP_FROM_CHARSET: + g_value_set_string (value, conv->from); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_charset_converter_class_init (GCharsetConverterClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_charset_converter_finalize; + gobject_class->get_property = g_charset_converter_get_property; + gobject_class->set_property = g_charset_converter_set_property; + + g_object_class_install_property (gobject_class, + PROP_TO_CHARSET, + g_param_spec_string ("to-charset", + P_("To Charset"), + P_("The character encoding to convert to"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_FROM_CHARSET, + g_param_spec_string ("from-charset", + P_("From Charset"), + P_("The character encoding to convert from"), + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + +} + +static void +g_charset_converter_init (GCharsetConverter *local) +{ +} + + +/** + * g_charset_converter_new: + * @to_charset: destination charset + * @from_charset: source charset + * @error: #GError for error reporting, or %NULL to ignore. + * + * Creates a new #GCharsetConverter. + * + * Returns: a new #GCharsetConverter or %NULL on error. + * + * Since: 2.24 + **/ +GCharsetConverter * +g_charset_converter_new (const gchar *to_charset, + const gchar *from_charset, + GError **error) +{ + GCharsetConverter *conv; + + conv = g_initable_new (G_TYPE_CHARSET_CONVERTER, + NULL, error, + "to-charset", to_charset, + "from-charset", from_charset, + NULL); + + return conv; +} + +static void +g_charset_converter_reset (GConverter *converter) +{ + GCharsetConverter *conv = G_CHARSET_CONVERTER (converter); + + if (conv->iconv == NULL) + { + g_warning ("Invalid object, not initialized"); + return; + } + + g_iconv (conv->iconv, NULL, NULL, NULL, NULL); +} + +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) +{ + GCharsetConverter *conv; + gsize res; + GConverterResult ret; + gchar *inbufp, *outbufp; + gsize in_left, out_left; + int errsv; + + conv = G_CHARSET_CONVERTER (converter); + + if (conv->iconv == NULL) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_INITIALIZED, + _("Invalid object, not initialized")); + 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; + + res = g_iconv (conv->iconv, + &inbufp, &in_left, + &outbufp, &out_left); + + *bytes_read = inbufp - (char *)inbuf; + *bytes_written = outbufp - (char *)outbuf; + + /* Don't report error if we converted anything */ + if (res == (gsize) -1 && *bytes_read == 0) + { + errsv = errno; + + switch (errsv) + { + case EINVAL: + /* Incomplete input text */ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, + _("Incomplete multibyte sequence in input")); + break; + + case E2BIG: + /* Not enough destination space */ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, + _("Not enough space in destination")); + break; + + case EILSEQ: + /* Invalid code sequence */ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + _("Invalid byte sequence in conversion input")); + break; + + default: + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Error during conversion: %s"), + g_strerror (errsv)); + break; + } + ret = G_CONVERTER_ERROR; + } + else + { + ret = G_CONVERTER_CONVERTED; + + if (in_left == 0 && + (flags & G_CONVERTER_INPUT_AT_END)) + ret = G_CONVERTER_FINISHED; + else if (in_left == 0 && + (flags & G_CONVERTER_FLUSH)) + ret = G_CONVERTER_FLUSHED; + } + + return ret; +} + +static void +g_charset_converter_iface_init (GConverterIface *iface) +{ + iface->convert = g_charset_converter_convert; + iface->reset = g_charset_converter_reset; +} + +static gboolean +g_charset_converter_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GCharsetConverter *conv; + + g_return_val_if_fail (G_IS_CHARSET_CONVERTER (initable), FALSE); + + conv = G_CHARSET_CONVERTER (initable); + + if (cancellable != NULL) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Cancellable initialization not supported")); + return FALSE; + } + + conv->iconv = + g_iconv_open (conv->to, conv->from); + + if (conv->iconv == NULL) + { + if (errno == EINVAL) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Conversion from character set '%s' to '%s' is not supported"), + conv->from, conv->to); + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Could not open converter from '%s' to '%s'"), + conv->from, conv->to); + return FALSE; + } + + return TRUE; +} + +static void +g_charset_converter_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_charset_converter_initable_init; +} + +#define __G_CHARSET_CONVERTER_C__ +#include "gioaliasdef.c" diff --git a/gio/gcharsetconverter.h b/gio/gcharsetconverter.h new file mode 100644 index 000000000..848eb45a2 --- /dev/null +++ b/gio/gcharsetconverter.h @@ -0,0 +1,52 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: Alexander Larsson + */ + +#ifndef __G_CHARSET_CONVERTER_H__ +#define __G_CHARSET_CONVERTER_H__ + +#include + +G_BEGIN_DECLS + +#define G_TYPE_CHARSET_CONVERTER (g_charset_converter_get_type ()) +#define G_CHARSET_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CHARSET_CONVERTER, GCharsetConverter)) +#define G_CHARSET_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CHARSET_CONVERTER, GCharsetConverterClass)) +#define G_IS_CHARSET_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CHARSET_CONVERTER)) +#define G_IS_CHARSET_CONVERTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CHARSET_CONVERTER)) +#define G_CHARSET_CONVERTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CHARSET_CONVERTER, GCharsetConverterClass)) + +typedef struct _GCharsetConverterClass GCharsetConverterClass; + +struct _GCharsetConverterClass +{ + GObjectClass parent_class; +}; + +GType g_charset_converter_get_type (void) G_GNUC_CONST; + +GCharsetConverter *g_charset_converter_new (const gchar *to_charset, + const gchar *from_charset, + GError **error); + +G_END_DECLS + +#endif /* __G_CHARSET_CONVERTER_H__ */ diff --git a/gio/gio.h b/gio/gio.h index d2e168eae..74cded9ed 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/gio/giotypes.h b/gio/giotypes.h index adb6341dd..5592252ca 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -38,6 +38,7 @@ typedef struct _GAsyncInitable GAsyncInitable; typedef struct _GBufferedInputStream GBufferedInputStream; typedef struct _GBufferedOutputStream GBufferedOutputStream; typedef struct _GCancellable GCancellable; +typedef struct _GCharsetConverter GCharsetConverter; typedef struct _GConverter GConverter; typedef struct _GDataInputStream GDataInputStream;