radiotap: Updates to the radiotap dissector to avoid confusion.
[metze/wireshark/wip.git] / epan / charsets.c
index 5a97dd5caaaae0d0289746683136df2de5cf03e5..bfb967e84bdb684062a52a556e11dc9546c7d457 100644 (file)
 guint8 *
 get_ascii_string(wmem_allocator_t *scope, const guint8 *ptr, gint length)
 {
-       wmem_strbuf_t *str;
+    wmem_strbuf_t *str;
 
-       str = wmem_strbuf_sized_new(scope, length+1, 0);
+    str = wmem_strbuf_sized_new(scope, length+1, 0);
 
-       while (length > 0) {
-               guint8 ch = *ptr;
+    while (length > 0) {
+        guint8 ch = *ptr;
 
-               if (ch < 0x80)
-                       wmem_strbuf_append_c(str, ch);
-               else
-                       wmem_strbuf_append_unichar(str, UNREPL);
-               ptr++;
-               length--;
-       }
+        if (ch < 0x80)
+            wmem_strbuf_append_c(str, ch);
+        else
+            wmem_strbuf_append_unichar(str, UNREPL);
+        ptr++;
+        length--;
+    }
 
-       return (guint8 *) wmem_strbuf_finalize(str);
+    return (guint8 *) wmem_strbuf_finalize(str);
 }
 
 /*
@@ -100,29 +100,29 @@ get_ascii_string(wmem_allocator_t *scope, const guint8 *ptr, gint length)
 guint8 *
 get_8859_1_string(wmem_allocator_t *scope, const guint8 *ptr, gint length)
 {
-       wmem_strbuf_t *str;
-
-       str = wmem_strbuf_sized_new(scope, length+1, 0);
-
-       while (length > 0) {
-               guint8 ch = *ptr;
-
-               if (ch < 0x80)
-                       wmem_strbuf_append_c(str, ch);
-               else {
-                       /*
-                        * Note: we assume here that the code points
-                        * 0x80-0x9F are used for C1 control characters,
-                        * and thus have the same value as the corresponding
-                        * Unicode code points.
-                        */
-                       wmem_strbuf_append_unichar(str, ch);
-               }
-               ptr++;
-               length--;
-       }
-
-       return (guint8 *) wmem_strbuf_finalize(str);
+    wmem_strbuf_t *str;
+
+    str = wmem_strbuf_sized_new(scope, length+1, 0);
+
+    while (length > 0) {
+        guint8 ch = *ptr;
+
+        if (ch < 0x80)
+            wmem_strbuf_append_c(str, ch);
+        else {
+            /*
+             * Note: we assume here that the code points
+             * 0x80-0x9F are used for C1 control characters,
+             * and thus have the same value as the corresponding
+             * Unicode code points.
+             */
+            wmem_strbuf_append_unichar(str, ch);
+        }
+        ptr++;
+        length--;
+    }
+
+    return (guint8 *) wmem_strbuf_finalize(str);
 }
 
 /*
@@ -473,7 +473,7 @@ const gunichar2 charset_table_cp437[0x80] = {
 };
 
 /*
- * Given a wmem scope, a pointer, and a length, and a translation table,
+ * Given a wmem scope, a pointer, a length, and a translation table,
  * treat the string of bytes referred to by the pointer and length as a
  * string encoded using one octet per character, with octets with the
  * high-order bit clear being ASCII and octets with the high-order bit
@@ -484,22 +484,22 @@ const gunichar2 charset_table_cp437[0x80] = {
 guint8 *
 get_unichar2_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const gunichar2 table[0x80])
 {
-       wmem_strbuf_t *str;
+    wmem_strbuf_t *str;
 
-       str = wmem_strbuf_sized_new(scope, length+1, 0);
+    str = wmem_strbuf_sized_new(scope, length+1, 0);
 
-       while (length > 0) {
-               guint8 ch = *ptr;
+    while (length > 0) {
+        guint8 ch = *ptr;
 
-               if (ch < 0x80)
-                       wmem_strbuf_append_c(str, ch);
-               else
-                       wmem_strbuf_append_unichar(str, table[ch-0x80]);
-               ptr++;
-               length--;
-       }
+        if (ch < 0x80)
+            wmem_strbuf_append_c(str, ch);
+        else
+            wmem_strbuf_append_unichar(str, table[ch-0x80]);
+        ptr++;
+        length--;
+    }
 
-       return (guint8 *) wmem_strbuf_finalize(str);
+    return (guint8 *) wmem_strbuf_finalize(str);
 }
 
 /*
@@ -521,26 +521,26 @@ get_unichar2_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, con
 guint8 *
 get_ucs_2_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const guint encoding)
 {
-       gunichar2      uchar;
-       gint           i;       /* Byte counter for string */
-       wmem_strbuf_t *strbuf;
-
-       strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
-
-       for(i = 0; i + 1 < length; i += 2) {
-               if (encoding == ENC_BIG_ENDIAN){
-                       uchar = pntoh16(ptr + i);
-               }else{
-                       uchar = pletoh16(ptr + i);
-               }
-               wmem_strbuf_append_unichar(strbuf, uchar);
-       }
-
-       /*
-        * XXX - if i < length, this means we were handed an odd
-        * number of bytes, so we're not a valid UCS-2 string.
-        */
-       return (guint8 *) wmem_strbuf_finalize(strbuf);
+    gunichar2      uchar;
+    gint           i;       /* Byte counter for string */
+    wmem_strbuf_t *strbuf;
+
+    strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
+
+    for(i = 0; i + 1 < length; i += 2) {
+        if (encoding == ENC_BIG_ENDIAN){
+            uchar = pntoh16(ptr + i);
+        }else{
+            uchar = pletoh16(ptr + i);
+        }
+        wmem_strbuf_append_unichar(strbuf, uchar);
+    }
+
+    /*
+     * XXX - if i < length, this means we were handed an odd
+     * number of bytes, so we're not a valid UCS-2 string.
+     */
+    return (guint8 *) wmem_strbuf_finalize(strbuf);
 }
 
 /*
@@ -548,6 +548,8 @@ get_ucs_2_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const
  * referred to by the pointer and length as a UTF-16 encoded string, and
  * return a pointer to a UTF-8 string, allocated with the wmem scope.
  *
+ * See RFC 2781 section 2.2.
+ *
  * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
  *
  * Specify length in bytes.
@@ -560,78 +562,78 @@ get_ucs_2_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const
 guint8 *
 get_utf_16_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const guint encoding)
 {
-       wmem_strbuf_t *strbuf;
-       gunichar2      uchar2, lead_surrogate;
-       gunichar       uchar;
-       gint           i;       /* Byte counter for string */
-
-       strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
-
-       for(i = 0; i + 1 < length; i += 2) {
-               if (encoding == ENC_BIG_ENDIAN)
-                       uchar2 = pntoh16(ptr + i);
-               else
-                       uchar2 = pletoh16(ptr + i);
-
-               if (IS_LEAD_SURROGATE(uchar2)) {
-                       /*
-                        * Lead surrogate.  Must be followed by
-                        * a trail surrogate.
-                        */
-                       i += 2;
-                       if (i + 1 >= length) {
-                               /*
-                                * Oops, string ends with a lead surrogate.
-                                * Ignore this for now.
-                                * XXX - insert "substitute" character?
-                                * Report the error in some other
-                                * fashion?
-                                */
-                               break;
-                       }
-                       lead_surrogate = uchar2;
-                       if (encoding == ENC_BIG_ENDIAN)
-                               uchar2 = pntoh16(ptr + i);
-                       else
-                               uchar2 = pletoh16(ptr + i);
-                       if (IS_TRAIL_SURROGATE(uchar2)) {
-                               /* Trail surrogate. */
-                               uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
-                               wmem_strbuf_append_unichar(strbuf, uchar);
-                       } else {
-                               /*
-                                * Not a trail surrogate.
-                                * Ignore the entire pair.
-                                * XXX - insert "substitute" character?
-                                * Report the error in some other
-                                * fashion?
-                                */
-                                ;
-                       }
-               } else {
-                       if (IS_TRAIL_SURROGATE(uchar2)) {
-                               /*
-                                * Trail surrogate without a preceding
-                                * lead surrogate.  Ignore it.
-                                * XXX - insert "substitute" character?
-                                * Report the error in some other
-                                * fashion?
-                                */
-                               ;
-                       } else {
-                               /*
-                                * Non-surrogate; just append it.
-                                */
-                               wmem_strbuf_append_unichar(strbuf, uchar2);
-                       }
-               }
-       }
-
-       /*
-        * XXX - if i < length, this means we were handed an odd
-        * number of bytes, so we're not a valid UTF-16 string.
-        */
-       return (guint8 *) wmem_strbuf_finalize(strbuf);
+    wmem_strbuf_t *strbuf;
+    gunichar2      uchar2, lead_surrogate;
+    gunichar       uchar;
+    gint           i;       /* Byte counter for string */
+
+    strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
+
+    for(i = 0; i + 1 < length; i += 2) {
+        if (encoding == ENC_BIG_ENDIAN)
+            uchar2 = pntoh16(ptr + i);
+        else
+            uchar2 = pletoh16(ptr + i);
+
+        if (IS_LEAD_SURROGATE(uchar2)) {
+            /*
+             * Lead surrogate.  Must be followed by
+             * a trail surrogate.
+             */
+            i += 2;
+            if (i + 1 >= length) {
+                /*
+                 * Oops, string ends with a lead surrogate.
+                 * Ignore this for now.
+                 * XXX - insert "substitute" character?
+                 * Report the error in some other
+                 * fashion?
+                 */
+                break;
+            }
+            lead_surrogate = uchar2;
+            if (encoding == ENC_BIG_ENDIAN)
+                uchar2 = pntoh16(ptr + i);
+            else
+                uchar2 = pletoh16(ptr + i);
+            if (IS_TRAIL_SURROGATE(uchar2)) {
+                /* Trail surrogate. */
+                uchar = SURROGATE_VALUE(lead_surrogate, uchar2);
+                wmem_strbuf_append_unichar(strbuf, uchar);
+            } else {
+                /*
+                 * Not a trail surrogate.
+                 * Ignore the entire pair.
+                 * XXX - insert "substitute" character?
+                 * Report the error in some other
+                 * fashion?
+                 */
+                 ;
+            }
+        } else {
+            if (IS_TRAIL_SURROGATE(uchar2)) {
+                /*
+                 * Trail surrogate without a preceding
+                 * lead surrogate.  Ignore it.
+                 * XXX - insert "substitute" character?
+                 * Report the error in some other
+                 * fashion?
+                 */
+                ;
+            } else {
+                /*
+                 * Non-surrogate; just append it.
+                 */
+                wmem_strbuf_append_unichar(strbuf, uchar2);
+            }
+        }
+    }
+
+    /*
+     * XXX - if i < length, this means we were handed an odd
+     * number of bytes, so we're not a valid UTF-16 string.
+     */
+    return (guint8 *) wmem_strbuf_finalize(strbuf);
 }
 
 /*
@@ -652,27 +654,27 @@ get_utf_16_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const
 guint8 *
 get_ucs_4_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const guint encoding)
 {
-       gunichar       uchar;
-       gint           i;       /* Byte counter for string */
-       wmem_strbuf_t *strbuf;
-
-       strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
-
-       for(i = 0; i + 3 < length; i += 4) {
-               if (encoding == ENC_BIG_ENDIAN)
-                       uchar = pntoh32(ptr + i);
-               else
-                       uchar = pletoh32(ptr + i);
-
-               wmem_strbuf_append_unichar(strbuf, uchar);
-       }
-
-       /*
-        * XXX - if i < length, this means we were handed a number
-        * of bytes that's not a multiple of 4, so we're not a valid
-        * UCS-4 string.
-        */
-       return (gchar *)wmem_strbuf_finalize(strbuf);
+    gunichar       uchar;
+    gint           i;       /* Byte counter for string */
+    wmem_strbuf_t *strbuf;
+
+    strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
+
+    for(i = 0; i + 3 < length; i += 4) {
+        if (encoding == ENC_BIG_ENDIAN)
+            uchar = pntoh32(ptr + i);
+        else
+            uchar = pletoh32(ptr + i);
+
+        wmem_strbuf_append_unichar(strbuf, uchar);
+    }
+
+    /*
+     * XXX - if i < length, this means we were handed a number
+     * of bytes that's not a multiple of 4, so we're not a valid
+     * UCS-4 string.
+     */
+    return (gchar *)wmem_strbuf_finalize(strbuf);
 }
 
 /*
@@ -737,171 +739,173 @@ GSMext_to_UNICHAR(guint8 c)
 static gboolean
 char_is_escape(unsigned char value)
 {
-       return (value == GN_CHAR_ESCAPE);
+    return (value == GN_CHAR_ESCAPE);
 }
 
 static gboolean
 handle_ts_23_038_char(wmem_strbuf_t *strbuf, guint8 code_point,
-                     gboolean saw_escape)
+                      gboolean saw_escape)
 {
-       gunichar       uchar;
-
-       if (char_is_escape(code_point)) {
-               /*
-                * XXX - if saw_escape is TRUE here, then this is
-                * the case where we escape to "another extension table",
-                * but TS 128 038 V11.0 doesn't specify such an extension
-                * table.
-                */
-               saw_escape = TRUE;
-       } else {
-               /*
-                * Have we seen an escape?
-                */
-               if (saw_escape) {
-                       saw_escape = FALSE;
-                       uchar = GSMext_to_UNICHAR(code_point);
-               } else {
-                       uchar = GSM_to_UNICHAR(code_point);
-               }
-               wmem_strbuf_append_unichar(strbuf, uchar);
-       }
-       return saw_escape;
+    gunichar       uchar;
+
+    if (char_is_escape(code_point)) {
+        /*
+         * XXX - if saw_escape is TRUE here, then this is
+         * the case where we escape to "another extension table",
+         * but TS 128 038 V11.0 doesn't specify such an extension
+         * table.
+         */
+        saw_escape = TRUE;
+    } else {
+        /*
+         * Have we seen an escape?
+         */
+        if (saw_escape) {
+            saw_escape = FALSE;
+            uchar = GSMext_to_UNICHAR(code_point);
+        } else {
+            uchar = GSM_to_UNICHAR(code_point);
+        }
+        wmem_strbuf_append_unichar(strbuf, uchar);
+    }
+    return saw_escape;
 }
 
 guint8 *
 get_ts_23_038_7bits_string(wmem_allocator_t *scope, const guint8 *ptr,
-       const gint bit_offset, gint no_of_chars)
+                           const gint bit_offset, gint no_of_chars)
 {
-       wmem_strbuf_t *strbuf;
-       gint           char_count;                  /* character counter for string */
-       guint8         in_byte, out_byte, rest = 0x00;
-       gboolean       saw_escape = FALSE;
-       int            bits;
-
-       strbuf = wmem_strbuf_sized_new(scope, no_of_chars+1, 0);
-
-       bits = bit_offset & 0x07;
-       if (!bits) {
-               bits = 7;
-       }
-
-       for(char_count = 0; char_count < no_of_chars;) {
-               /* Get the next byte from the string. */
-               in_byte = *ptr;
-               ptr++;
-
-               /*
-                * Combine the bits we've accumulated with bits from
-                * that byte to make a 7-bit code point.
-                */
-               out_byte = ((in_byte & GN_BYTE_MASK) << (7 - bits)) | rest;
-
-               /*
-                * Leftover bits used in that code point.
-                */
-               rest = in_byte >> bits;
-
-               /*
-                * If we don't start from 0th bit, we shouldn't go to the
-                * next char. Under *out_num we have now 0 and under Rest -
-                * _first_ part of the char.
-                */
-               if (char_count || (bits == 7)) {
-                       saw_escape = handle_ts_23_038_char(strbuf, out_byte,
-                           saw_escape);
-                       char_count++;
-               }
-
-               /*
-                * After reading 7 octets we have read 7 full characters
-                * but we have 7 bits as well. This is the next character.
-                */
-               if ((bits == 1) && (char_count < no_of_chars)) {
-                       saw_escape = handle_ts_23_038_char(strbuf, rest,
-                           saw_escape);
-                       char_count++;
-                       bits = 7;
-                       rest = 0x00;
-               } else
-                       bits--;
-       }
-
-       if (saw_escape) {
-               /*
-                * Escape not followed by anything.
-                *
-                * XXX - for now, show the escape as a REPLACEMENT
-                * CHARACTER.
-                */
-               wmem_strbuf_append_unichar(strbuf, UNREPL);
-       }
-
-       return (guint8 *)wmem_strbuf_finalize(strbuf);
+    wmem_strbuf_t *strbuf;
+    gint           char_count;                  /* character counter for string */
+    guint8         in_byte, out_byte, rest = 0x00;
+    const guint8  *start_ptr = ptr;
+    gboolean       saw_escape = FALSE;
+    int            bits;
+
+    strbuf = wmem_strbuf_sized_new(scope, no_of_chars+1, 0);
+
+    bits = bit_offset & 0x07;
+    if (!bits) {
+        bits = 7;
+    }
+
+    for(char_count = 0; char_count < no_of_chars; ptr++) {
+        /* Get the next byte from the string. */
+        in_byte = *ptr;
+
+        /*
+         * Combine the bits we've accumulated with bits from
+         * that byte to make a 7-bit code point.
+         */
+        out_byte = ((in_byte & GN_BYTE_MASK) << (7 - bits)) | rest;
+
+        /*
+         * Leftover bits used in that code point.
+         */
+        rest = in_byte >> bits;
+
+        /*
+         * If we don't start from 0th bit, we shouldn't go to the
+         * next char. Under *out_num we have now 0 and under Rest -
+         * _first_ part of the char.
+         */
+        if ((start_ptr != ptr) || (bits == 7)) {
+            saw_escape = handle_ts_23_038_char(strbuf, out_byte,
+                saw_escape);
+            char_count++;
+        }
+
+        /*
+         * After reading 7 octets we have read 7 full characters
+         * but we have 7 bits as well. This is the next character.
+         */
+        if ((bits == 1) && (char_count < no_of_chars)) {
+            saw_escape = handle_ts_23_038_char(strbuf, rest,
+                saw_escape);
+            char_count++;
+            bits = 7;
+            rest = 0x00;
+        } else {
+            bits--;
+        }
+    }
+
+    if (saw_escape) {
+        /*
+         * Escape not followed by anything.
+         *
+         * XXX - for now, show the escape as a REPLACEMENT
+         * CHARACTER.
+         */
+        wmem_strbuf_append_unichar(strbuf, UNREPL);
+    }
+
+    return (guint8 *)wmem_strbuf_finalize(strbuf);
 }
 
 guint8 *
 get_ascii_7bits_string(wmem_allocator_t *scope, const guint8 *ptr,
-       const gint bit_offset, gint no_of_chars)
+                       const gint bit_offset, gint no_of_chars)
 {
-       wmem_strbuf_t *strbuf;
-       gint           char_count;                  /* character counter for string */
-       guint8         in_byte, out_byte, rest = 0x00;
-       int            bits;
-
-       bits = bit_offset & 0x07;
-       if (!bits) {
-               bits = 7;
-       }
-
-       strbuf = wmem_strbuf_sized_new(scope, no_of_chars+1, 0);
-       for(char_count = 0; char_count < no_of_chars;) {
-               /* Get the next byte from the string. */
-               in_byte = *ptr;
-               ptr++;
-
-               /*
-                * Combine the bits we've accumulated with bits from
-                * that byte to make a 7-bit code point.
-                */
-               out_byte = (in_byte >> (8 - bits)) | rest;
-
-               /*
-                * Leftover bits used in that code point.
-                */
-               rest = (in_byte << (bits - 1)) & 0x7f;
-
-               /*
-                * If we don't start from 0th bit, we shouldn't go to the
-                * next char. Under *out_num we have now 0 and under Rest -
-                * _first_ part of the char.
-                */
-               if (char_count || (bits == 7)) {
-                       wmem_strbuf_append_c(strbuf, out_byte);
-                       char_count++;
-               }
-
-               /*
-                * After reading 7 octets we have read 7 full characters
-                * but we have 7 bits as well. This is the next character.
-                */
-               if ((bits == 1) && (char_count < no_of_chars)) {
-                       wmem_strbuf_append_c(strbuf, rest);
-                       char_count++;
-                       bits = 7;
-                       rest = 0x00;
-               } else
-                       bits--;
-       }
-
-       return (guint8 *)wmem_strbuf_finalize(strbuf);
+    wmem_strbuf_t *strbuf;
+    gint           char_count;                  /* character counter for string */
+    guint8         in_byte, out_byte, rest = 0x00;
+    const guint8  *start_ptr = ptr;
+    int            bits;
+
+    bits = bit_offset & 0x07;
+    if (!bits) {
+        bits = 7;
+    }
+
+    strbuf = wmem_strbuf_sized_new(scope, no_of_chars+1, 0);
+    for(char_count = 0; char_count < no_of_chars; ptr++) {
+        /* Get the next byte from the string. */
+        in_byte = *ptr;
+
+        /*
+         * Combine the bits we've accumulated with bits from
+         * that byte to make a 7-bit code point.
+         */
+        out_byte = (in_byte >> (8 - bits)) | rest;
+
+        /*
+         * Leftover bits used in that code point.
+         */
+        rest = (in_byte << (bits - 1)) & 0x7f;
+
+        /*
+         * If we don't start from 0th bit, we shouldn't go to the
+         * next char. Under *out_num we have now 0 and under Rest -
+         * _first_ part of the char.
+         */
+        if ((start_ptr != ptr) || (bits == 7)) {
+            wmem_strbuf_append_c(strbuf, out_byte);
+            char_count++;
+        }
+
+        /*
+         * After reading 7 octets we have read 7 full characters
+         * but we have 7 bits as well. This is the next character.
+         */
+        if ((bits == 1) && (char_count < no_of_chars)) {
+            wmem_strbuf_append_c(strbuf, rest);
+            char_count++;
+            bits = 7;
+            rest = 0x00;
+        } else {
+            bits--;
+        }
+    }
+
+    return (guint8 *)wmem_strbuf_finalize(strbuf);
 }
 
 /* ASCII/EBCDIC conversion tables from
  * http://www.room42.com/store/computer_center/code_tables.shtml
  */
 #if 0
-static guint8 ASCII_translate_EBCDIC [ 256 ] = {
+static const guint8 ASCII_translate_EBCDIC [ 256 ] = {
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
     0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
@@ -956,39 +960,39 @@ ASCII_to_EBCDIC1(guint8 c)
 }
 #endif
 
-static guint8 EBCDIC_translate_ASCII [ 256 ] = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-    0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
-    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
-    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
-    0x2E, 0x2E, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
-    0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x2E, 0x3F,
-    0x20, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x2E, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
-    0x26, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
-    0x2D, 0x2F, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
-    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
-    0x2E, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
-    0x69, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
-    0x72, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
-    0x7A, 0x2E, 0x2E, 0x2E, 0x5B, 0x2E, 0x2E,
-    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x2E, 0x2E, 0x2E, 0x2E, 0x5D, 0x2E, 0x2E,
-    0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
-    0x49, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,
-    0x52, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x5C, 0x2E, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
-    0x5A, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
-    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
-    0x39, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E
+static const guint8 EBCDIC_translate_ASCII [ 256 ] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+    0x2E, 0x2E, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x2E, 0x3F,
+    0x20, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x2E, 0x2E, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
+    0x26, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x2E, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
+    0x2D, 0x2F, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x2E, 0x7C, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
+    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x2E, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
+    0x2E, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+    0x68, 0x69, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+    0x71, 0x72, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+    0x79, 0x7A, 0x2E, 0x2E, 0x2E, 0x5B, 0x2E, 0x2E,
+    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x5D, 0x2E, 0x2E,
+    0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+    0x51, 0x52, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x5C, 0x2E, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+    0x59, 0x5A, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E
 };
 
 void
@@ -1010,28 +1014,401 @@ EBCDIC_to_ASCII1(guint8 c)
     return EBCDIC_translate_ASCII[c];
 }
 
+/* Tables for EBCDIC code pages */
+
+/* EBCDIC common; based on the table in appendix H of ESA/370 Principles
+   of Operation, but with some code points that don't correspond to
+   the same characters in code pages 037 and 1158 mapped to REPLACEMENT
+   CHARACTER - there may be more code points of that sort */
+const gunichar2 charset_table_ebcdic[256] = {
+    0x0000, 0x0001, 0x0002, 0x0003, 0x009c, 0x0009, 0x0086, 0x007f,
+    0x0097, 0x008d, 0x008e, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+    0x0010, 0x0011, 0x0012, 0x0013, 0x009d, 0x0085, 0x0008, 0x0087,
+    0x0018, 0x0019, 0x0092, 0x008f, 0x001c, 0x001d, 0x001e, 0x001f,
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000a, 0x0017, 0x001b,
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x0005, 0x0006, 0x0007,
+    UNREPL, UNREPL, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
+    0x0098, 0x0099, 0x009a, 0x009b, 0x0014, 0x0015, UNREPL, 0x001a,
+    0x0020, 0x00a0, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, UNREPL, UNREPL, 0x002e, 0x003c, 0x0028, 0x002b, UNREPL,
+    0x0026, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, UNREPL, UNREPL, 0x0024, 0x002a, 0x0029, 0x003b, UNREPL,
+    0x002d, 0x002f, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, UNREPL, UNREPL, 0x002c, 0x0025, 0x005f, 0x003e, 0x003f,
+    UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, 0x0060, 0x003a, 0x0023, 0x0040, 0x0027, 0x003d, 0x0022,
+    UNREPL, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+    0x0068, 0x0069, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070,
+    0x0071, 0x0072, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, 0x007e, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
+    0x0079, 0x007a, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    0x007b, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+    0x0048, 0x0049, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    0x007d, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050,
+    0x0051, 0x0052, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    0x005c, UNREPL, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
+    0x0059, 0x005a, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+    0x0038, 0x0039, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL, UNREPL,
+};
+
+/* EBCDIC code page 037 */
+const gunichar2 charset_table_ebcdic_cp037[256] = {
+    0x0000, 0x0001, 0x0002, 0x0003, 0x009c, 0x0009, 0x0086, 0x007f,
+    0x0097, 0x008d, 0x008e, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+    0x0010, 0x0011, 0x0012, 0x0013, 0x009d, 0x0085, 0x0008, 0x0087,
+    0x0018, 0x0019, 0x0092, 0x008f, 0x001c, 0x001d, 0x001e, 0x001f,
+    0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000a, 0x0017, 0x001b,
+    0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x0005, 0x0006, 0x0007,
+    0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004,
+    0x0098, 0x0099, 0x009a, 0x009b, 0x0014, 0x0015, 0x009e, 0x001a,
+    0x0020, 0x00a0, 0x00e2, 0x00e4, 0x00e0, 0x00e1, 0x00e3, 0x00e5,
+    0x00e7, 0x00f1, 0x00a2, 0x002e, 0x003c, 0x0028, 0x002b, 0x007c,
+    0x0026, 0x00e9, 0x00ea, 0x00eb, 0x00e8, 0x00ed, 0x00ee, 0x00ef,
+    0x00ec, 0x00df, 0x0021, 0x0024, 0x002a, 0x0029, 0x003b, 0x00ac,
+    0x002d, 0x002f, 0x00c2, 0x00c4, 0x00c0, 0x00c1, 0x00c3, 0x00c5,
+    0x00c7, 0x00d1, 0x00a6, 0x002c, 0x0025, 0x005f, 0x003e, 0x003f,
+    0x00f8, 0x00c9, 0x00ca, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf,
+    0x00cc, 0x0060, 0x003a, 0x0023, 0x0040, 0x0027, 0x003d, 0x0022,
+    0x00d8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+    0x0068, 0x0069, 0x00ab, 0x00bb, 0x00f0, 0x00fd, 0x00fe, 0x00b1,
+    0x00b0, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070,
+    0x0071, 0x0072, 0x00aa, 0x00ba, 0x00e6, 0x00b8, 0x00c6, 0x00a4,
+    0x00b5, 0x007e, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
+    0x0079, 0x007a, 0x00a1, 0x00bf, 0x00d0, 0x00dd, 0x00de, 0x00ae,
+    0x005e, 0x00a3, 0x00a5, 0x00b7, 0x00a9, 0x00a7, 0x00b6, 0x00bc,
+    0x00bd, 0x00be, 0x005b, 0x005d, 0x00af, 0x00a8, 0x00b4, 0x00d7,
+    0x007b, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+    0x0048, 0x0049, 0x00ad, 0x00f4, 0x00f6, 0x00f2, 0x00f3, 0x00f5,
+    0x007d, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050,
+    0x0051, 0x0052, 0x00b9, 0x00fb, 0x00fc, 0x00f9, 0x00fa, 0x00ff,
+    0x005c, 0x00f7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
+    0x0059, 0x005a, 0x00b2, 0x00d4, 0x00d6, 0x00d2, 0x00d3, 0x00d5,
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+    0x0038, 0x0039, 0x00b3, 0x00db, 0x00dc, 0x00d9, 0x00da, 0x009f,
+};
+
 /*
- * Given a wmem scope, a pointer, and a length, and a translation table,
+ * Given a wmem scope, a pointer, a length, and a translation table,
  * treat the string of bytes referred to by the pointer and length as a
- * string encoded in EBCDIC using one octet per character, and return a
+ * string encoded using one octet per character, with octets being
+ * mapped by the translation table to 2-byte Unicode Basic Multilingual
+ * Plane characters (including REPLACEMENT CHARACTER), and return a
  * pointer to a UTF-8 string, allocated using the wmem scope.
  */
 guint8 *
-get_ebcdic_string(wmem_allocator_t *scope, const guint8 *ptr, gint length)
+get_nonascii_unichar2_string(wmem_allocator_t *scope, const guint8 *ptr, gint length, const gunichar2 table[256])
 {
-       wmem_strbuf_t *str;
+    wmem_strbuf_t *str;
+
+    str = wmem_strbuf_sized_new(scope, length+1, 0);
+
+    while (length > 0) {
+        guint8 ch = *ptr;
+
+        wmem_strbuf_append_unichar(str, table[ch]);
+        ptr++;
+        length--;
+    }
 
-       str = wmem_strbuf_sized_new(scope, length+1, 0);
+    return (guint8 *) wmem_strbuf_finalize(str);
+}
+
+/* T.61 to UTF-8 conversion table from OpenLDAP project
+ * http://www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=libraries/libldap/t61.c;hb=HEAD
+ */
+static const gunichar2 t61_tab[] = {
+    0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007,
+    0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f,
+    0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017,
+    0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f,
+    0x020, 0x021, 0x022, 0x000, 0x000, 0x025, 0x026, 0x027,
+    0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f,
+    0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037,
+    0x038, 0x039, 0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x03f,
+    0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047,
+    0x048, 0x049, 0x04a, 0x04b, 0x04c, 0x04d, 0x04e, 0x04f,
+    0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057,
+    0x058, 0x059, 0x05a, 0x05b, 0x000, 0x05d, 0x000, 0x05f,
+    0x000, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067,
+    0x068, 0x069, 0x06a, 0x06b, 0x06c, 0x06d, 0x06e, 0x06f,
+    0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077,
+    0x078, 0x079, 0x07a, 0x000, 0x07c, 0x000, 0x000, 0x07f,
+    0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087,
+    0x088, 0x089, 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f,
+    0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097,
+    0x098, 0x099, 0x09a, 0x09b, 0x09c, 0x09d, 0x09e, 0x09f,
+    0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x024, 0x0a5, 0x023, 0x0a7,
+    0x0a4, 0x000, 0x000, 0x0ab, 0x000, 0x000, 0x000, 0x000,
+    0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0d7, 0x0b5, 0x0b6, 0x0b7,
+    0x0f7, 0x000, 0x000, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf,
+    0x000, 0x300, 0x301, 0x302, 0x303, 0x304, 0x306, 0x307,
+    0x308, 0x000, 0x30a, 0x327, 0x332, 0x30b, 0x328, 0x30c,
+    0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
+    0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
+    0x2126, 0xc6, 0x0d0, 0x0aa, 0x126, 0x000, 0x132, 0x13f,
+    0x141, 0x0d8, 0x152, 0x0ba, 0x0de, 0x166, 0x14a, 0x149,
+    0x138, 0x0e6, 0x111, 0x0f0, 0x127, 0x131, 0x133, 0x140,
+    0x142, 0x0f8, 0x153, 0x0df, 0x0fe, 0x167, 0x14b, 0x000
+};
+
+typedef gunichar2 wvec16[16];
+typedef gunichar2 wvec32[32];
+
+/* Substitutions when 0xc1-0xcf appears by itself or with space 0x20 */
+static const wvec16 accents = {
+    0x000, 0x060, 0x0b4, 0x05e, 0x07e, 0x0af, 0x2d8, 0x2d9,
+    0x0a8, 0x000, 0x2da, 0x0b8, 0x000, 0x2dd, 0x2db, 0x2c7};
+
+/* In the following tables, base characters commented in (parentheses)
+ * are not defined by T.61 but are mapped anyway since their Unicode
+ * composite exists.
+ */
+
+/* Grave accented chars AEIOU (NWY) */
+static const wvec32 c1_vec1 = {
+    /* Upper case */
+    0, 0xc0, 0, 0, 0, 0xc8, 0, 0, 0, 0xcc, 0, 0, 0, 0, 0x1f8, 0xd2,
+    0, 0, 0, 0, 0, 0xd9, 0, 0x1e80, 0, 0x1ef2, 0, 0, 0, 0, 0, 0};
+static const wvec32 c1_vec2 = {
+    /* Lower case */
+    0, 0xe0, 0, 0, 0, 0xe8, 0, 0, 0, 0xec, 0, 0, 0, 0, 0x1f9, 0xf2,
+    0, 0, 0, 0, 0, 0xf9, 0, 0x1e81, 0, 0x1ef3, 0, 0, 0, 0, 0, 0};
+
+static const wvec32 *c1_grave[] = {
+    NULL, NULL, &c1_vec1, &c1_vec2, NULL, NULL, NULL, NULL
+};
 
-       while (length > 0) {
-               guint8 ch = *ptr;
+/* Acute accented chars AEIOUYCLNRSZ (GKMPW) */
+static const wvec32 c2_vec1 = {
+    /* Upper case */
+    0, 0xc1, 0, 0x106, 0, 0xc9, 0, 0x1f4,
+    0, 0xcd, 0, 0x1e30, 0x139, 0x1e3e, 0x143, 0xd3,
+    0x1e54, 0, 0x154, 0x15a, 0, 0xda, 0, 0x1e82,
+    0, 0xdd, 0x179, 0, 0, 0, 0, 0};
+static const wvec32 c2_vec2 = {
+    /* Lower case */
+    0, 0xe1, 0, 0x107, 0, 0xe9, 0, 0x1f5,
+    0, 0xed, 0, 0x1e31, 0x13a, 0x1e3f, 0x144, 0xf3,
+    0x1e55, 0, 0x155, 0x15b, 0, 0xfa, 0, 0x1e83,
+    0, 0xfd, 0x17a, 0, 0, 0, 0, 0};
+static const wvec32 c2_vec3 = {
+    /* (AE and ae) */
+    0, 0x1fc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0x1fd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static const wvec32 *c2_acute[] = {
+    NULL, NULL, &c2_vec1, &c2_vec2, NULL, NULL, NULL, &c2_vec3
+};
+
+/* Circumflex AEIOUYCGHJSW (Z) */
+static const wvec32 c3_vec1 = {
+    /* Upper case */
+    0, 0xc2, 0, 0x108, 0, 0xca, 0, 0x11c,
+    0x124, 0xce, 0x134, 0, 0, 0, 0, 0xd4,
+    0, 0, 0, 0x15c, 0, 0xdb, 0, 0x174,
+    0, 0x176, 0x1e90, 0, 0, 0, 0, 0};
+static const wvec32 c3_vec2 = {
+    /* Lower case */
+    0, 0xe2, 0, 0x109, 0, 0xea, 0, 0x11d,
+    0x125, 0xee, 0x135, 0, 0, 0, 0, 0xf4,
+    0, 0, 0, 0x15d, 0, 0xfb, 0, 0x175,
+    0, 0x177, 0x1e91, 0, 0, 0, 0, 0};
+static const wvec32 *c3_circumflex[] = {
+    NULL, NULL, &c3_vec1, &c3_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Tilde AIOUN (EVY) */
+static const wvec32 c4_vec1 = {
+    /* Upper case */
+    0, 0xc3, 0, 0, 0, 0x1ebc, 0, 0, 0, 0x128, 0, 0, 0, 0, 0xd1, 0xd5,
+    0, 0, 0, 0, 0, 0x168, 0x1e7c, 0, 0, 0x1ef8, 0, 0, 0, 0, 0, 0};
+static const wvec32 c4_vec2 = {
+    /* Lower case */
+    0, 0xe3, 0, 0, 0, 0x1ebd, 0, 0, 0, 0x129, 0, 0, 0, 0, 0xf1, 0xf5,
+    0, 0, 0, 0, 0, 0x169, 0x1e7d, 0, 0, 0x1ef9, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c4_tilde[] = {
+    NULL, NULL, &c4_vec1, &c4_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Macron AEIOU (YG) */
+static const wvec32 c5_vec1 = {
+    /* Upper case */
+    0, 0x100, 0, 0, 0, 0x112, 0, 0x1e20, 0, 0x12a, 0, 0, 0, 0, 0, 0x14c,
+    0, 0, 0, 0, 0, 0x16a, 0, 0, 0, 0x232, 0, 0, 0, 0, 0, 0};
+static const wvec32 c5_vec2 = {
+    /* Lower case */
+    0, 0x101, 0, 0, 0, 0x113, 0, 0x1e21, 0, 0x12b, 0, 0, 0, 0, 0, 0x14d,
+    0, 0, 0, 0, 0, 0x16b, 0, 0, 0, 0x233, 0, 0, 0, 0, 0, 0};
+static const wvec32 c5_vec3 = {
+    /* (AE and ae) */
+    0, 0x1e2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0x1e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c5_macron[] = {
+    NULL, NULL, &c5_vec1, &c5_vec2, NULL, NULL, NULL, &c5_vec3
+};
+
+/* Breve AUG (EIO) */
+static const wvec32 c6_vec1 = {
+    /* Upper case */
+    0, 0x102, 0, 0, 0, 0x114, 0, 0x11e, 0, 0x12c, 0, 0, 0, 0, 0, 0x14e,
+    0, 0, 0, 0, 0, 0x16c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 c6_vec2 = {
+    /* Lower case */
+    0, 0x103, 0, 0, 0, 0x115, 0, 0x11f, 0, 0x12d, 0, 0, 0, 0, 0, 0x14f,
+    0, 0, 0, 0, 0, 0x16d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c6_breve[] = {
+    NULL, NULL, &c6_vec1, &c6_vec2, NULL, NULL, NULL, NULL
+};
 
-               wmem_strbuf_append_unichar(str, EBCDIC_translate_ASCII[ch]);
-               ptr++;
-               length--;
-       }
+/* Dot Above CEGIZ (AOBDFHMNPRSTWXY) */
+static const wvec32 c7_vec1 = {
+    /* Upper case */
+    0, 0x226, 0x1e02, 0x10a, 0x1e0a, 0x116, 0x1e1e, 0x120,
+    0x1e22, 0x130, 0, 0, 0, 0x1e40, 0x1e44, 0x22e,
+    0x1e56, 0, 0x1e58, 0x1e60, 0x1e6a, 0, 0, 0x1e86,
+    0x1e8a, 0x1e8e, 0x17b, 0, 0, 0, 0, 0};
+static const wvec32 c7_vec2 = {
+    /* Lower case */
+    0, 0x227, 0x1e03, 0x10b, 0x1e0b, 0x117, 0x1e1f, 0x121,
+    0x1e23, 0, 0, 0, 0, 0x1e41, 0x1e45, 0x22f,
+    0x1e57, 0, 0x1e59, 0x1e61, 0x1e6b, 0, 0, 0x1e87,
+    0x1e8b, 0x1e8f, 0x17c, 0, 0, 0, 0, 0};
+static const wvec32 *c7_dotabove[] = {
+    NULL, NULL, &c7_vec1, &c7_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Diaeresis AEIOUY (HWXt) */
+static const wvec32 c8_vec1 = {
+    /* Upper case */
+    0, 0xc4, 0, 0, 0, 0xcb, 0, 0, 0x1e26, 0xcf, 0, 0, 0, 0, 0, 0xd6,
+    0, 0, 0, 0, 0, 0xdc, 0, 0x1e84, 0x1e8c, 0x178, 0, 0, 0, 0, 0, 0};
+static const wvec32 c8_vec2 = {
+    /* Lower case */
+    0, 0xe4, 0, 0, 0, 0xeb, 0, 0, 0x1e27, 0xef, 0, 0, 0, 0, 0, 0xf6,
+    0, 0, 0, 0, 0x1e97, 0xfc, 0, 0x1e85, 0x1e8d, 0xff, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c8_diaeresis[] = {
+    NULL, NULL, &c8_vec1, &c8_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Ring Above AU (wy) */
+static const wvec32 ca_vec1 = {
+    /* Upper case */
+    0, 0xc5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0x16e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 ca_vec2 = {
+    /* Lower case */
+    0, 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0x16f, 0, 0x1e98, 0, 0x1e99, 0, 0, 0, 0, 0, 0};
+static const wvec32 *ca_ringabove[] = {
+    NULL, NULL, &ca_vec1, &ca_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Cedilla CGKLNRST (EDH) */
+static const wvec32 cb_vec1 = {
+    /* Upper case */
+    0, 0, 0, 0xc7, 0x1e10, 0x228, 0, 0x122,
+    0x1e28, 0, 0, 0x136, 0x13b, 0, 0x145, 0,
+    0, 0, 0x156, 0x15e, 0x162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 cb_vec2 = {
+    /* Lower case */
+    0, 0, 0, 0xe7, 0x1e11, 0x229, 0, 0x123,
+    0x1e29, 0, 0, 0x137, 0x13c, 0, 0x146, 0,
+    0, 0, 0x157, 0x15f, 0x163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *cb_cedilla[] = {
+    NULL, NULL, &cb_vec1, &cb_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Double Acute Accent OU */
+static const wvec32 cd_vec1 = {
+    /* Upper case */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x150,
+    0, 0, 0, 0, 0, 0x170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 cd_vec2 = {
+    /* Lower case */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x151,
+    0, 0, 0, 0, 0, 0x171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *cd_doubleacute[] = {
+    NULL, NULL, &cd_vec1, &cd_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Ogonek AEIU (O) */
+static const wvec32 ce_vec1 = {
+    /* Upper case */
+    0, 0x104, 0, 0, 0, 0x118, 0, 0, 0, 0x12e, 0, 0, 0, 0, 0, 0x1ea,
+    0, 0, 0, 0, 0, 0x172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 ce_vec2 = {
+    /* Lower case */
+    0, 0x105, 0, 0, 0, 0x119, 0, 0, 0, 0x12f, 0, 0, 0, 0, 0, 0x1eb,
+    0, 0, 0, 0, 0, 0x173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *ce_ogonek[] = {
+    NULL, NULL, &ce_vec1, &ce_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Caron CDELNRSTZ (AIOUGKjH) */
+static const wvec32 cf_vec1 = {
+    /* Upper case */
+    0, 0x1cd, 0, 0x10c, 0x10e, 0x11a, 0, 0x1e6,
+    0x21e, 0x1cf, 0, 0x1e8, 0x13d, 0, 0x147, 0x1d1,
+    0, 0, 0x158, 0x160, 0x164, 0x1d3, 0, 0,
+    0, 0, 0x17d, 0, 0, 0, 0, 0};
+static const wvec32 cf_vec2 = {
+    /* Lower case */
+    0, 0x1ce, 0, 0x10d, 0x10f, 0x11b, 0, 0x1e7,
+    0x21f, 0x1d0, 0x1f0, 0x1e9, 0x13e, 0, 0x148, 0x1d2,
+    0, 0, 0x159, 0x161, 0x165, 0x1d4, 0, 0,
+    0, 0, 0x17e, 0, 0, 0, 0, 0};
+static const wvec32 *cf_caron[] = {
+    NULL, NULL, &cf_vec1, &cf_vec2, NULL, NULL, NULL, NULL
+};
+
+static const wvec32 **cx_tab[] = {
+    NULL, c1_grave, c2_acute, c3_circumflex, c4_tilde, c5_macron,
+    c6_breve, c7_dotabove, c8_diaeresis, NULL, ca_ringabove,
+    cb_cedilla, NULL, cd_doubleacute, ce_ogonek, cf_caron };
+
+guint8 *
+get_t61_string(wmem_allocator_t *scope, const guint8 *ptr, gint length)
+{
+    gint           i;
+    const guint8  *c;
+    wmem_strbuf_t *strbuf;
+
+    strbuf = wmem_strbuf_sized_new(scope, length+1, 0);
+
+    for (i = 0, c = ptr; i < length; c++, i++) {
+        if (!t61_tab[*c]) {
+            wmem_strbuf_append_unichar(strbuf, UNREPL);
+        } else if ((*c & 0xf0) == 0xc0) {
+            gint j = *c & 0x0f;
+            /* If this is the end of the string, or if the base
+             * character is just a space, treat this as a regular
+             * spacing character.
+             */
+            if ((!c[1] || c[1] == 0x20) && accents[j]) {
+                wmem_strbuf_append_unichar(strbuf, accents[j]);
+            } else if (cx_tab[j] && cx_tab[j][c[1]>>5] &&
+                /* We have a composite mapping for this pair */
+                       (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) {
+                wmem_strbuf_append_unichar(strbuf, (*cx_tab[j][c[1]>>5])[c[1]&0x1f]);
+            } else {
+                /* No mapping, just swap it around so the base
+                 * character comes first.
+                 */
+                wmem_strbuf_append_unichar(strbuf, c[1]);
+                wmem_strbuf_append_unichar(strbuf, t61_tab[*c]);
+            }
+            c++; i++;
+            continue;
+        } else {
+            wmem_strbuf_append_unichar(strbuf, t61_tab[*c]);
+        }
+    }
 
-       return (guint8 *) wmem_strbuf_finalize(str);
+    return (guint8 *)wmem_strbuf_finalize(strbuf);
 }
 
 /*