]> git.sur5r.net Git - glabels/blobdiff - src/merge-text.c
Imported Upstream version 3.4.0
[glabels] / src / merge-text.c
index c60b26900cfb5da0b9a92afec876992c774c6e5d..f24ae011124efc15c94bd1421fa0e349cc3517fa 100644 (file)
 #include "merge-text.h"
 
 #include <stdio.h>
+#include <errno.h>
+#include <string.h>
 
 #include "debug.h"
 
 #define LINE_BUF_LEN 1024
 
-
+/*
+ * Unicode handling.
+ *  The default encoding assumption is that files are in the system encoding.
+ * However, files are checked for a Unicode BOM (Byte Order Mark), which if found
+ * alters the manner in which files are handled.
+ */
+enum UnicodeEncoding {
+        SYSTEM_ENCODING,
+        UTF8,
+        UTF16_LE,
+        UTF16_BE,
+        UTF32_LE,
+        UTF32_BE
+};
+        
 /*===========================================*/
 /* Private types                             */
 /*===========================================*/
 
 struct _glMergeTextPrivate {
 
-       gchar             delim;
+        gchar             delim;
         gboolean          line1_has_keys;
 
-       FILE             *fp;
+        enum UnicodeEncoding   encoding;
+        GIConv             g_iconverter;
+        gchar              char_buf[MB_LEN_MAX];
+        gsize              buf_pos;
+        gsize              buf_len;
+
+        FILE             *fp;
 
         GPtrArray        *keys;
         gint              n_fields_max;
 };
 
 enum {
-       LAST_SIGNAL
+        LAST_SIGNAL
 };
 
 enum {
-       ARG_0,
-       ARG_DELIM,
-       ARG_LINE1_HAS_KEYS
+        ARG_0,
+        ARG_DELIM,
+        ARG_LINE1_HAS_KEYS
 };
 
 
@@ -67,14 +89,14 @@ enum {
 static void           gl_merge_text_finalize        (GObject          *object);
 
 static void           gl_merge_text_set_property    (GObject          *object,
-                                                    guint             param_id,
-                                                    const GValue     *value,
-                                                    GParamSpec       *pspec);
+                                                     guint             param_id,
+                                                     const GValue     *value,
+                                                     GParamSpec       *pspec);
 
 static void           gl_merge_text_get_property    (GObject          *object,
-                                                    guint             param_id,
-                                                    GValue           *value,
-                                                    GParamSpec       *pspec);
+                                                     guint             param_id,
+                                                     GValue           *value,
+                                                     GParamSpec       *pspec);
 
 static gchar         *key_from_index                (glMergeText      *merge_text,
                                                      gint              i_field);
@@ -86,10 +108,10 @@ static void           gl_merge_text_open            (glMerge          *merge);
 static void           gl_merge_text_close           (glMerge          *merge);
 static glMergeRecord *gl_merge_text_get_record      (glMerge          *merge);
 static void           gl_merge_text_copy            (glMerge          *dst_merge,
-                                                    const glMerge    *src_merge);
+                                                     const glMerge    *src_merge);
 
-static GList         *parse_line                    (FILE             *fp,
-                                                    gchar             delim);
+static GList         *parse_line                    (glMergeText       *merge_text,
+                                                     gchar             delim);
 static void           free_fields                   (GList           **fields);
 
 
@@ -103,72 +125,72 @@ G_DEFINE_TYPE (glMergeText, gl_merge_text, GL_TYPE_MERGE)
 static void
 gl_merge_text_class_init (glMergeTextClass *class)
 {
-       GObjectClass *object_class = G_OBJECT_CLASS (class);
-       glMergeClass *merge_class  = GL_MERGE_CLASS (class);
+        GObjectClass *object_class = G_OBJECT_CLASS (class);
+        glMergeClass *merge_class  = GL_MERGE_CLASS (class);
 
-       gl_debug (DEBUG_MERGE, "START");
+        gl_debug (DEBUG_MERGE, "START");
 
-       gl_merge_text_parent_class = g_type_class_peek_parent (class);
+        gl_merge_text_parent_class = g_type_class_peek_parent (class);
 
-       object_class->set_property = gl_merge_text_set_property;
-       object_class->get_property = gl_merge_text_get_property;
+        object_class->set_property = gl_merge_text_set_property;
+        object_class->get_property = gl_merge_text_get_property;
 
-       g_object_class_install_property
+        g_object_class_install_property
                 (object_class,
                  ARG_DELIM,
                  g_param_spec_char ("delim", NULL, NULL,
-                                   0, 0x7F, ',',
-                                   (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+                                    0, 0x7F, ',',
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
 
-       g_object_class_install_property
+        g_object_class_install_property
                 (object_class,
                  ARG_LINE1_HAS_KEYS,
                  g_param_spec_boolean ("line1_has_keys", NULL, NULL,
                                        FALSE,
                                        (G_PARAM_READABLE | G_PARAM_WRITABLE)));
 
-       object_class->finalize = gl_merge_text_finalize;
+        object_class->finalize = gl_merge_text_finalize;
 
-       merge_class->get_key_list    = gl_merge_text_get_key_list;
-       merge_class->get_primary_key = gl_merge_text_get_primary_key;
-       merge_class->open            = gl_merge_text_open;
-       merge_class->close           = gl_merge_text_close;
-       merge_class->get_record      = gl_merge_text_get_record;
-       merge_class->copy            = gl_merge_text_copy;
+        merge_class->get_key_list    = gl_merge_text_get_key_list;
+        merge_class->get_primary_key = gl_merge_text_get_primary_key;
+        merge_class->open            = gl_merge_text_open;
+        merge_class->close           = gl_merge_text_close;
+        merge_class->get_record      = gl_merge_text_get_record;
+        merge_class->copy            = gl_merge_text_copy;
 
-       gl_debug (DEBUG_MERGE, "END");
+        gl_debug (DEBUG_MERGE, "END");
 }
 
 
 static void
 gl_merge_text_init (glMergeText *merge_text)
 {
-       gl_debug (DEBUG_MERGE, "START");
+        gl_debug (DEBUG_MERGE, "START");
 
-       merge_text->priv = g_new0 (glMergeTextPrivate, 1);
+        merge_text->priv = g_new0 (glMergeTextPrivate, 1);
 
         merge_text->priv->keys = g_ptr_array_new ();
 
-       gl_debug (DEBUG_MERGE, "END");
+        gl_debug (DEBUG_MERGE, "END");
 }
 
 
 static void
 gl_merge_text_finalize (GObject *object)
 {
-       glMergeText *merge_text = GL_MERGE_TEXT (object);
+        glMergeText *merge_text = GL_MERGE_TEXT (object);
 
-       gl_debug (DEBUG_MERGE, "START");
+        gl_debug (DEBUG_MERGE, "START");
 
-       g_return_if_fail (object && GL_IS_MERGE_TEXT (object));
+        g_return_if_fail (object && GL_IS_MERGE_TEXT (object));
 
         clear_keys (merge_text);
         g_ptr_array_free (merge_text->priv->keys, TRUE);
-       g_free (merge_text->priv);
+        g_free (merge_text->priv);
 
-       G_OBJECT_CLASS (gl_merge_text_parent_class)->finalize (object);
+        G_OBJECT_CLASS (gl_merge_text_parent_class)->finalize (object);
 
-       gl_debug (DEBUG_MERGE, "END");
+        gl_debug (DEBUG_MERGE, "END");
 }
 
 
@@ -177,27 +199,27 @@ gl_merge_text_finalize (GObject *object)
 /*--------------------------------------------------------------------------*/
 static void
 gl_merge_text_set_property (GObject      *object,
-                           guint         param_id,
-                           const GValue *value,
-                           GParamSpec   *pspec)
+                            guint         param_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
 {
-       glMergeText *merge_text;
+        glMergeText *merge_text;
 
-       merge_text = GL_MERGE_TEXT (object);
+        merge_text = GL_MERGE_TEXT (object);
 
-       switch (param_id) {
+        switch (param_id) {
 
-       case ARG_DELIM:
-               merge_text->priv->delim = g_value_get_schar (value);
-               gl_debug (DEBUG_MERGE, "ARG \"delim\" = \"%c\"",
-                         merge_text->priv->delim);
-               break;
+        case ARG_DELIM:
+                merge_text->priv->delim = g_value_get_schar (value);
+                gl_debug (DEBUG_MERGE, "ARG \"delim\" = \"%c\"",
+                          merge_text->priv->delim);
+                break;
 
         case ARG_LINE1_HAS_KEYS:
                 merge_text->priv->line1_has_keys = g_value_get_boolean (value);
-               gl_debug (DEBUG_MERGE, "ARG \"line1_has_keys\" = \"%d\"",
-                         merge_text->priv->line1_has_keys);
-               break;
+                gl_debug (DEBUG_MERGE, "ARG \"line1_has_keys\" = \"%d\"",
+                          merge_text->priv->line1_has_keys);
+                break;
 
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -213,19 +235,19 @@ gl_merge_text_set_property (GObject      *object,
 /*--------------------------------------------------------------------------*/
 static void
 gl_merge_text_get_property (GObject     *object,
-                           guint        param_id,
-                           GValue      *value,
-                           GParamSpec  *pspec)
+                            guint        param_id,
+                            GValue      *value,
+                            GParamSpec  *pspec)
 {
-       glMergeText *merge_text;
+        glMergeText *merge_text;
 
-       merge_text = GL_MERGE_TEXT (object);
+        merge_text = GL_MERGE_TEXT (object);
 
-       switch (param_id) {
+        switch (param_id) {
 
-       case ARG_DELIM:
-               g_value_set_schar (value, merge_text->priv->delim);
-               break;
+        case ARG_DELIM:
+                g_value_set_schar (value, merge_text->priv->delim);
+                break;
 
         case ARG_LINE1_HAS_KEYS:
                 g_value_set_boolean (value, merge_text->priv->line1_has_keys);
@@ -281,13 +303,13 @@ clear_keys (glMergeText      *merge_text)
 static GList *
 gl_merge_text_get_key_list (const glMerge *merge)
 {
-       glMergeText   *merge_text;
-       gint           i_field, n_fields;
-       GList         *key_list;
-       
-       gl_debug (DEBUG_MERGE, "BEGIN");
+        glMergeText   *merge_text;
+        gint           i_field, n_fields;
+        GList         *key_list;
+        
+        gl_debug (DEBUG_MERGE, "BEGIN");
 
-       merge_text = GL_MERGE_TEXT (merge);
+        merge_text = GL_MERGE_TEXT (merge);
 
         if ( merge_text->priv->line1_has_keys )
         {
@@ -304,9 +326,9 @@ gl_merge_text_get_key_list (const glMerge *merge)
                 key_list = g_list_append (key_list, key_from_index(merge_text, i_field));
         }
 
-       gl_debug (DEBUG_MERGE, "END");
+        gl_debug (DEBUG_MERGE, "END");
 
-       return key_list;
+        return key_list;
 }
 
 
@@ -316,10 +338,143 @@ gl_merge_text_get_key_list (const glMerge *merge)
 static gchar *
 gl_merge_text_get_primary_key (const glMerge *merge)
 {
-       /* For now, let's always assume the first column is the primary key. */
+        /* For now, let's always assume the first column is the primary key. */
         return key_from_index (GL_MERGE_TEXT (merge), 0);
 }
 
+/*--------------------------------------------------------------------------*/
+/* Read the byte order marks to determine unicode encoding, if any.         */
+/* See https://en.wikipedia.org/wiki/Byte_order_mark                        */
+/*--------------------------------------------------------------------------*/
+static enum UnicodeEncoding
+gl_read_encoding(FILE* fp) {
+        enum UnicodeEncoding encoding;
+        gchar ch, ch2, ch3, ch4;
+        ch = getc(fp);
+
+        if (ch == '\xff') {
+                ch2 = getc(fp);
+
+                if (ch2 == '\xfe') {
+                        ch3 = getc(fp);
+                        ch4 = getc(fp);
+                        if (ch3 == '\0' && ch4 == '\0') {
+                                encoding = UTF32_LE;
+                        } else {
+                                ungetc(ch4, fp);
+                                ungetc(ch3, fp);
+                                encoding = UTF16_LE;
+                        }
+                } else {
+                        ungetc(ch2, fp);
+                        ungetc(ch, fp);
+                        encoding = SYSTEM_ENCODING;
+                }
+        } else if (ch == '\xfe') {
+                ch2 = getc(fp);
+                if (ch2 == '\xff') {
+                        encoding = UTF16_BE;
+                } else {
+                        ungetc(ch2, fp);
+                        ungetc(ch, fp);
+                        encoding = SYSTEM_ENCODING;
+                }
+        } else if (ch == '\0') {
+                ch2 = getc(fp);
+                ch3 = getc(fp);
+                ch4 = getc(fp);
+                if (ch2 == '\0' && ch3 == '\xfe' && ch4 == '\xff') {
+                        encoding = UTF32_BE;
+                } else {
+                        ungetc(ch4, fp);
+                        ungetc(ch3, fp);
+                        ungetc(ch2, fp);
+                        encoding = SYSTEM_ENCODING;
+                }
+        } else if (ch == '\xef') {
+                ch2 = getc(fp);
+                if (ch2 == '\xbb') {
+                        ch3 = getc(fp);
+                        if (ch3 == '\xbf') {
+                                encoding = UTF8;
+                        } else {
+                                ungetc(ch3, fp);
+                                ungetc(ch2, fp);
+                                ungetc(ch, fp);
+                                encoding = SYSTEM_ENCODING;
+                        }
+                } else {
+                        ungetc(ch2, fp);
+                        ungetc(ch, fp);
+                        encoding = SYSTEM_ENCODING;
+                }
+        } else {
+                ungetc(ch, fp);
+                encoding = SYSTEM_ENCODING;
+        }
+        return encoding;
+}
+
+/*
+ * gLabels get-character routine for possibly Unicode text files.
+ * If the source has a byte order mark (BOM) indicating a Unicode file, 
+ * g_iconv is used to convert input characters to GDK-standard UTF8 format.
+ */
+
+static gchar
+gl_getc(glMergeText *merge_text) {
+        if (merge_text->priv->buf_pos < merge_text->priv->buf_len) {
+                return merge_text->priv->char_buf[merge_text->priv->buf_pos++];
+        } else if (merge_text->priv->encoding == SYSTEM_ENCODING || 
+                   merge_text->priv->encoding == UTF8) {
+                return getc(merge_text->priv->fp);
+        } else {
+                /*
+                 * a UTF-16 stream might include surrogates, which encode
+                 * characters in successive 16-bit units. If we read a
+                 * leading surrogate, read in the trailing one as well for 
+                 * processing.
+                 */
+                gchar wcbuf[4];
+                size_t result;
+                gchar* outbufp;
+                int hob_offset;
+                int unit_len;
+                switch (merge_text->priv->encoding) {
+                case UTF16_BE:
+                        hob_offset = 0;
+                        unit_len = 2;
+                        break;
+                case UTF16_LE:
+                        hob_offset = 1;
+                        unit_len = 2;
+                        break;
+                case UTF32_BE:
+                case UTF32_LE:
+                        hob_offset = -1;
+                        unit_len = 4;
+                        break;
+                }
+                gsize nBytes = fread(wcbuf, 1, unit_len, merge_text->priv->fp);
+                if (nBytes == 0)
+                        return EOF;
+                if (hob_offset >= 0 && (wcbuf[hob_offset] & 0xfd) == 0xd8) {
+                        nBytes += fread(wcbuf+unit_len, 1, unit_len, merge_text->priv->fp);
+                }
+                gchar* wcbufp = wcbuf;
+                outbufp = merge_text->priv->char_buf;
+                gsize buflen = sizeof(merge_text->priv->char_buf);
+                result = g_iconv(merge_text->priv->g_iconverter, &wcbufp, &nBytes,
+                                 &outbufp, &buflen);
+                if (result == EOF) {
+                        g_warning("g_iconv: %s", strerror(errno));
+                }
+                merge_text->priv->buf_len = outbufp - merge_text->priv->char_buf;
+                merge_text->priv->buf_pos = 0;
+                return merge_text->priv->char_buf[merge_text->priv->buf_pos++];
+        }
+}
+
 
 /*--------------------------------------------------------------------------*/
 /* Open merge source.                                                       */
@@ -327,25 +482,54 @@ gl_merge_text_get_primary_key (const glMerge *merge)
 static void
 gl_merge_text_open (glMerge *merge)
 {
-       glMergeText *merge_text;
-       gchar       *src;
+        glMergeText *merge_text;
+        gchar       *src;
 
         GList       *line1_fields;
         GList       *p;
 
-       merge_text = GL_MERGE_TEXT (merge);
+        merge_text = GL_MERGE_TEXT (merge);
 
-       src = gl_merge_get_src (merge);
+        src = gl_merge_get_src (merge);
 
-       if (src != NULL)
+        if (src != NULL)
         {
-               if (g_utf8_strlen(src, -1) == 1 && src[0] == '-')
-                       merge_text->priv->fp = stdin;
-               else
-                       merge_text->priv->fp = fopen (src, "r");
-
+                if (g_utf8_strlen(src, -1) == 1 && src[0] == '-') {
+                        merge_text->priv->fp = stdin;
+                        merge_text->priv->encoding = SYSTEM_ENCODING;
+                } else {
+                        if ((merge_text->priv->fp = fopen (src, "r")) != NULL) {
+                                merge_text->priv->encoding = gl_read_encoding(merge_text->priv->fp);
+                        } else {
+                                g_warning("gl_merge_text_open: %s (%s)",
+                                        strerror(errno), src);
+                        }
+                }
                 g_free (src);
 
+                gchar* in_codeset = NULL;
+                switch (merge_text->priv->encoding) {
+                case UTF8:
+                case SYSTEM_ENCODING:
+                        break;
+                case UTF16_BE:
+                        in_codeset = "UTF-16BE";
+                        break;
+                case UTF16_LE:
+                        in_codeset = "UTF-16LE";
+                        break;
+                case UTF32_BE:
+                        in_codeset = "UTF-32BE";
+                        break;
+                case UTF32_LE:
+                        in_codeset = "UTF-32LE";
+                        break;
+                }
+                if (in_codeset != NULL) {
+                        merge_text->priv->g_iconverter = g_iconv_open("UTF8", in_codeset);
+                        /* Since we define both codesets, we should always be able to open the converter */
+                        g_assert(merge_text->priv->g_iconverter != (GIConv)-1);
+                }
                 clear_keys (merge_text);
                 merge_text->priv->n_fields_max = 0;
 
@@ -355,7 +539,7 @@ gl_merge_text_open (glMerge *merge)
                          * Extract keys from first line and discard line
                          */
 
-                        line1_fields = parse_line (merge_text->priv->fp, merge_text->priv->delim);
+                        line1_fields = parse_line (merge_text, merge_text->priv->delim);
                         for ( p = line1_fields; p != NULL; p = p->next )
                         {
                                 g_ptr_array_add (merge_text->priv->keys, g_strdup (p->data));
@@ -363,7 +547,7 @@ gl_merge_text_open (glMerge *merge)
                         free_fields (&line1_fields);
                 }
 
-       }
+        }
 
 
 }
@@ -375,16 +559,20 @@ gl_merge_text_open (glMerge *merge)
 static void
 gl_merge_text_close (glMerge *merge)
 {
-       glMergeText *merge_text;
+        glMergeText *merge_text;
 
-       merge_text = GL_MERGE_TEXT (merge);
+        merge_text = GL_MERGE_TEXT (merge);
 
-       if (merge_text->priv->fp != NULL) {
+        if (merge_text->priv->fp != NULL) {
 
-               fclose (merge_text->priv->fp);
-               merge_text->priv->fp = NULL;
+                fclose (merge_text->priv->fp);
+                merge_text->priv->fp = NULL;
 
-       }
+        }
+        if (merge_text->priv->g_iconverter != 0) {
+                g_iconv_close(merge_text->priv->g_iconverter);
+                merge_text->priv->g_iconverter = 0;
+        }
 }
 
 
@@ -394,46 +582,48 @@ gl_merge_text_close (glMerge *merge)
 static glMergeRecord *
 gl_merge_text_get_record (glMerge *merge)
 {
-       glMergeText   *merge_text;
-       gchar          delim;
-       FILE          *fp;
-       glMergeRecord *record;
-       GList         *fields, *p;
-       gint           i_field;
-       glMergeField  *field;
+        glMergeText   *merge_text;
+        gchar          delim;
+        glMergeRecord *record;
+        GList         *fields, *p;
+        gint           i_field;
+        glMergeField  *field;
 
-       merge_text = GL_MERGE_TEXT (merge);
+        merge_text = GL_MERGE_TEXT (merge);
 
-       delim = merge_text->priv->delim;
-       fp    = merge_text->priv->fp;
+        delim = merge_text->priv->delim;
 
-       fields = parse_line (fp, delim);
-       if ( fields == NULL ) {
-               return NULL;
-       }
+        fields = parse_line (merge_text, delim);
+        if ( fields == NULL ) {
+                return NULL;
+        }
 
-       record = g_new0 (glMergeRecord, 1);
-       record->select_flag = TRUE;
-       for (p=fields, i_field=0; p != NULL; p=p->next, i_field++) {
+        record = g_new0 (glMergeRecord, 1);
+        record->select_flag = TRUE;
+        for (p=fields, i_field=0; p != NULL; p=p->next, i_field++) {
 
-               field = g_new0 (glMergeField, 1);
+                field = g_new0 (glMergeField, 1);
                 field->key = key_from_index (merge_text, i_field);
 #ifndef CSV_ALWAYS_UTF8
-               field->value = g_locale_to_utf8 (p->data, -1, NULL, NULL, NULL);
+                if (merge_text->priv->encoding == SYSTEM_ENCODING) {
+                        field->value = g_locale_to_utf8 (p->data, -1, NULL, NULL, NULL);
+                } else {
+                        field->value = g_strdup (p->data);
+                }
 #else
-               field->value = g_strdup (p->data);
+                field->value = g_strdup (p->data);
 #endif
 
-               record->field_list = g_list_append (record->field_list, field);
-       }
-       free_fields (&fields);
+                record->field_list = g_list_append (record->field_list, field);
+        }
+        free_fields (&fields);
 
         if ( i_field > merge_text->priv->n_fields_max )
         {
                 merge_text->priv->n_fields_max = i_field;
         }
 
-       return record;
+        return record;
 }
 
 
@@ -442,17 +632,17 @@ gl_merge_text_get_record (glMerge *merge)
 /*---------------------------------------------------------------------------*/
 static void
 gl_merge_text_copy (glMerge       *dst_merge,
-                   const glMerge *src_merge)
+                    const glMerge *src_merge)
 {
-       glMergeText *dst_merge_text;
-       glMergeText *src_merge_text;
+        glMergeText *dst_merge_text;
+        glMergeText *src_merge_text;
         gint         i;
 
-       dst_merge_text = GL_MERGE_TEXT (dst_merge);
-       src_merge_text = GL_MERGE_TEXT (src_merge);
+        dst_merge_text = GL_MERGE_TEXT (dst_merge);
+        src_merge_text = GL_MERGE_TEXT (src_merge);
 
-       dst_merge_text->priv->delim          = src_merge_text->priv->delim;
-       dst_merge_text->priv->line1_has_keys = src_merge_text->priv->line1_has_keys;
+        dst_merge_text->priv->delim          = src_merge_text->priv->delim;
+        dst_merge_text->priv->line1_has_keys = src_merge_text->priv->line1_has_keys;
 
         for ( i=0; i < src_merge_text->priv->keys->len; i++ )
         {
@@ -460,7 +650,7 @@ gl_merge_text_copy (glMerge       *dst_merge,
                                  g_strdup ((gchar *)g_ptr_array_index (src_merge_text->priv->keys, i)));
         }
 
-       dst_merge_text->priv->n_fields_max   = src_merge_text->priv->n_fields_max;
+        dst_merge_text->priv->n_fields_max   = src_merge_text->priv->n_fields_max;
 }
 
 
@@ -482,53 +672,53 @@ gl_merge_text_copy (glMerge       *dst_merge,
 /* empty field.  Returns empty (NULL) when done.                             */
 /*---------------------------------------------------------------------------*/
 static GList *
-parse_line (FILE  *fp,
-           gchar  delim )
+parse_line (glMergeText* merge_text,
+            gchar  delim )
 {
-       GList   *list;
-       GString *field;
-       gint     c;
-       enum { DELIM,
+        GList   *list;
+        GString *field;
+        gint     c;
+        enum { DELIM,
                QUOTED, QUOTED_QUOTE1, QUOTED_ESCAPED,
                SIMPLE, SIMPLE_ESCAPED,
                DONE } state;
 
-       if (fp == NULL) {
-               return NULL;
-       }
-              
-       state = DELIM;
+        if (merge_text->priv->fp == NULL) {
+                return NULL;
+        }
+               
+        state = DELIM;
         list  = NULL;
-       field = g_string_new( "" );
-       while ( state != DONE ) {
-               c=getc (fp);
-
-               switch (state) {
-
-               case DELIM:
-                       switch (c) {
-                       case '\n':
-                               /* last field is empty. */
-                               list = g_list_append (list, g_strdup (""));
-                               state = DONE;
-                               break;
-                       case '\r':
-                               /* ignore */
-                               state = DELIM;
-                               break;
-                       case EOF:
+        field = g_string_new( "" );
+        while ( state != DONE ) {
+                c=gl_getc (merge_text);
+
+                switch (state) {
+
+                case DELIM:
+                        switch (c) {
+                        case '\n':
+                                /* last field is empty. */
+                                list = g_list_append (list, g_strdup (""));
+                                state = DONE;
+                                break;
+                        case '\r':
+                                /* ignore */
+                                state = DELIM;
+                                break;
+                        case EOF:
                                 /* end of file, no more lines. */
-                               state = DONE;
-                               break;
-                       case '"':
+                                state = DONE;
+                                break;
+                        case '"':
                                 /* start a quoted field. */
-                               state = QUOTED;
-                               break;
-                       case '\\':
+                                state = QUOTED;
+                                break;
+                        case '\\':
                                 /* simple field, but 1st character is an escape. */
-                               state = SIMPLE_ESCAPED;
-                               break;
-                       default:
+                                state = SIMPLE_ESCAPED;
+                                break;
+                        default:
                                 if ( c == delim )
                                 {
                                         /* field is empty. */
@@ -541,50 +731,50 @@ parse_line (FILE  *fp,
                                         field = g_string_append_c (field, c);
                                         state = SIMPLE;
                                 }
-                               break;
-                       }
-                       break;
-
-               case QUOTED:
-                       switch (c) {
-                       case EOF:
-                               /* File ended mid way through quoted item, truncate field. */
-                               list = g_list_append (list, g_strdup (field->str));
-                               state = DONE;
-                               break;
-                       case '"':
+                                break;
+                        }
+                        break;
+
+                case QUOTED:
+                        switch (c) {
+                        case EOF:
+                                /* File ended mid way through quoted item, truncate field. */
+                                list = g_list_append (list, g_strdup (field->str));
+                                state = DONE;
+                                break;
+                        case '"':
                                 /* Possible end of field, but could be 1st of a pair. */
-                               state = QUOTED_QUOTE1;
-                               break;
-                       case '\\':
+                                state = QUOTED_QUOTE1;
+                                break;
+                        case '\\':
                                 /* Escape next character, or special escape, e.g. \n. */
-                               state = QUOTED_ESCAPED;
-                               break;
-                       default:
+                                state = QUOTED_ESCAPED;
+                                break;
+                        default:
                                 /* Use character literally. */
-                               field = g_string_append_c (field, c);
-                               break;
-                       }
-                       break;
-
-               case QUOTED_QUOTE1:
-                       switch (c) {
-                       case '\n':
-                       case EOF:
-                               /* line or file ended after quoted item */
-                               list = g_list_append (list, g_strdup (field->str));
-                               state = DONE;
-                               break;
-                       case '"':
-                               /* second quote, insert and stay quoted. */
-                               field = g_string_append_c (field, c);
-                               state = QUOTED;
-                               break;
-                       case '\r':
-                               /* ignore and go to fallback */
-                               state = SIMPLE;
-                               break;
-                       default:
+                                field = g_string_append_c (field, c);
+                                break;
+                        }
+                        break;
+
+                case QUOTED_QUOTE1:
+                        switch (c) {
+                        case '\n':
+                        case EOF:
+                                /* line or file ended after quoted item */
+                                list = g_list_append (list, g_strdup (field->str));
+                                state = DONE;
+                                break;
+                        case '"':
+                                /* second quote, insert and stay quoted. */
+                                field = g_string_append_c (field, c);
+                                state = QUOTED;
+                                break;
+                        case '\r':
+                                /* ignore and go to fallback */
+                                state = SIMPLE;
+                                break;
+                        default:
                                 if ( c == delim )
                                 {
                                         /* end of field. */
@@ -598,52 +788,52 @@ parse_line (FILE  *fp,
                                         field = g_string_append_c (field, c);
                                         state = SIMPLE;
                                 }
-                               break;
-                       }
-                       break;
-
-               case QUOTED_ESCAPED:
-                       switch (c) {
-                       case EOF:
-                               /* File ended mid way through quoted item */
-                               list = g_list_append (list, g_strdup (field->str));
-                               state = DONE;
-                               break;
+                                break;
+                        }
+                        break;
+
+                case QUOTED_ESCAPED:
+                        switch (c) {
+                        case EOF:
+                                /* File ended mid way through quoted item */
+                                list = g_list_append (list, g_strdup (field->str));
+                                state = DONE;
+                                break;
                         case 'n':
                                 /* Decode "\n" as newline. */
-                               field = g_string_append_c (field, '\n');
-                               state = QUOTED;
-                               break;
+                                field = g_string_append_c (field, '\n');
+                                state = QUOTED;
+                                break;
                         case 't':
                                 /* Decode "\t" as tab. */
-                               field = g_string_append_c (field, '\t');
-                               state = QUOTED;
-                               break;
-                       default:
+                                field = g_string_append_c (field, '\t');
+                                state = QUOTED;
+                                break;
+                        default:
                                 /* Use character literally. */
-                               field = g_string_append_c (field, c);
-                               state = QUOTED;
-                               break;
-                       }
-                       break;
-
-               case SIMPLE:
-                       switch (c) {
-                       case '\n':
-                       case EOF:
-                               /* line or file ended */
-                               list = g_list_append (list, g_strdup (field->str));
-                               state = DONE;
-                               break;
-                       case '\r':
-                               /* ignore */
-                               state = SIMPLE;
-                               break;
-                       case '\\':
+                                field = g_string_append_c (field, c);
+                                state = QUOTED;
+                                break;
+                        }
+                        break;
+
+                case SIMPLE:
+                        switch (c) {
+                        case '\n':
+                        case EOF:
+                                /* line or file ended */
+                                list = g_list_append (list, g_strdup (field->str));
+                                state = DONE;
+                                break;
+                        case '\r':
+                                /* ignore */
+                                state = SIMPLE;
+                                break;
+                        case '\\':
                                 /* Escape next character, or special escape, e.g. \n. */
-                               state = SIMPLE_ESCAPED;
-                               break;
-                       default:
+                                state = SIMPLE_ESCAPED;
+                                break;
+                        default:
                                 if ( c == delim )
                                 {
                                         /* end of field. */
@@ -657,44 +847,44 @@ parse_line (FILE  *fp,
                                         field = g_string_append_c (field, c);
                                         state = SIMPLE;
                                 }
-                               break;
-                       }
-                       break;
-
-               case SIMPLE_ESCAPED:
-                       switch (c) {
-                       case EOF:
-                               /* File ended mid way through quoted item */
-                               list = g_list_append (list, g_strdup (field->str));
-                               state = DONE;
-                               break;
+                                break;
+                        }
+                        break;
+
+                case SIMPLE_ESCAPED:
+                        switch (c) {
+                        case EOF:
+                                /* File ended mid way through quoted item */
+                                list = g_list_append (list, g_strdup (field->str));
+                                state = DONE;
+                                break;
                         case 'n':
                                 /* Decode "\n" as newline. */
-                               field = g_string_append_c (field, '\n');
-                               state = SIMPLE;
-                               break;
+                                field = g_string_append_c (field, '\n');
+                                state = SIMPLE;
+                                break;
                         case 't':
                                 /* Decode "\t" as tab. */
-                               field = g_string_append_c (field, '\t');
-                               state = SIMPLE;
-                               break;
-                       default:
+                                field = g_string_append_c (field, '\t');
+                                state = SIMPLE;
+                                break;
+                        default:
                                 /* Use character literally. */
-                               field = g_string_append_c (field, c);
-                               state = SIMPLE;
-                               break;
-                       }
-                       break;
+                                field = g_string_append_c (field, c);
+                                state = SIMPLE;
+                                break;
+                        }
+                        break;
 
-               default:
-                       g_assert_not_reached();
-                       break;
-               }
+                default:
+                        g_assert_not_reached();
+                        break;
+                }
 
-       }
-       g_string_free( field, TRUE );
+        }
+        g_string_free( field, TRUE );
 
-       return list;
+        return list;
 }
 
 
@@ -704,15 +894,16 @@ parse_line (FILE  *fp,
 void
 free_fields (GList ** list)
 {
-       GList *p;
+        GList *p;
 
-       for (p = *list; p != NULL; p = p->next) {
-               g_free (p->data);
-               p->data = NULL;
-       }
+        for ( p = *list; p != NULL; p = p->next )
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
 
-       g_list_free (*list);
-       *list = NULL;
+        g_list_free (*list);
+        *list = NULL;
 }