]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_utfconv.c
xnu-792.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_utfconv.c
index 4629660720852a32834914f3df9315625919de00..01a49889e3b5aab73923cac07eee2c2c836daaac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
+ /*
+       Includes Unicode 3.2 decomposition code derived from Core Foundation
+ */
+
 #include <sys/param.h>
 #include <sys/utfconv.h>
 #include <sys/errno.h>
 #define SP_HIGH_FIRST  0xD800UL
 #define SP_HIGH_LAST   0xDBFFUL
 #define SP_LOW_FIRST   0xDC00UL
-#define SP_LOW_LAST            0xDFFFUL
+#define SP_LOW_LAST    0xDFFFUL
 
 
-static u_int16_t ucs_decompose(u_int16_t, u_int16_t *);
+#include "vfs_utfconvdata.h"
 
-static u_int16_t ucs_combine(u_int16_t base, u_int16_t comb);
+
+/*
+ * Test for a combining character.
+ *
+ * Similar to __CFUniCharIsNonBaseCharacter except that
+ * unicode_combinable also includes Hangul Jamo characters.
+ */
+static inline int
+unicode_combinable(u_int16_t character)
+{
+       const u_int8_t *bitmap = __CFUniCharCombiningBitmap;
+       u_int8_t value;
+
+       if (character < 0x0300)
+               return (0);
+
+       value = bitmap[(character >> 8) & 0xFF];
+
+       if (value == 0xFF) {
+               return (1);
+       } else if (value) {
+               bitmap = bitmap + ((value - 1) * 32) + 256;
+               return (bitmap[(character & 0xFF) / 8] & (1 << (character % 8)) ? 1 : 0);
+       }
+       return (0);
+}
+
+/*
+ * Test for a precomposed character.
+ *
+ * Similar to __CFUniCharIsDecomposableCharacter.
+ */
+static inline int
+unicode_decomposeable(u_int16_t character) {
+       const u_int8_t *bitmap = __CFUniCharDecomposableBitmap;
+       u_int8_t value;
+       
+       if (character < 0x00C0)
+               return (0);
+
+       value = bitmap[(character >> 8) & 0xFF];
+
+       if (value == 0xFF) {
+               return (1);
+       } else if (value) {
+               bitmap = bitmap + ((value - 1) * 32) + 256;
+               return (bitmap[(character & 0xFF) / 8] & (1 << (character % 8)) ? 1 : 0);
+       }
+       return (0);
+}
+
+static int unicode_decompose(u_int16_t character, u_int16_t *convertedChars);
+
+static u_int16_t unicode_combine(u_int16_t base, u_int16_t combining);
 
 
 char utf_extrabytes[32] = {
@@ -133,7 +191,9 @@ utf8_encodestr(const u_int16_t * ucsp, size_t ucslen, u_int8_t * utf8p,
        u_int8_t * bufstart;
        u_int8_t * bufend;
        u_int16_t ucs_ch;
-       u_int16_t extra[2] = {0};
+       u_int16_t * chp = NULL;
+       u_int16_t sequence[8];
+       int extra = 0;
        int charcnt;
        int swapbytes = (flags & UTF_REVERSE_ENDIAN);
        int nullterm  = ((flags & UTF_NO_NULL_TERM) == 0);
@@ -147,19 +207,18 @@ utf8_encodestr(const u_int16_t * ucsp, size_t ucslen, u_int8_t * utf8p,
        charcnt = ucslen / 2;
 
        while (charcnt-- > 0) {
-               if (!decompose)
-                       ucs_ch = swapbytes ? NXSwapShort(*ucsp++) : *ucsp++;
-               else if (extra[0]) {
-                       ucs_ch = extra[0]; extra[0] = 0;
-               } else if (extra[1]) {
-                       ucs_ch = extra[1]; extra[1] = 0;
+               if (extra > 0) {
+                       --extra;
+                       ucs_ch = *chp++;
                } else {
                        ucs_ch = swapbytes ? NXSwapShort(*ucsp++) : *ucsp++;
-                       ucs_ch = ucs_decompose(ucs_ch, &extra[0]);
-                       if (extra[0])
-                               charcnt++;
-                       if (extra[1])
-                               charcnt++;
+
+                       if (decompose && unicode_decomposeable(ucs_ch)) {
+                               extra = unicode_decompose(ucs_ch, sequence) - 1;
+                               charcnt += extra;
+                               ucs_ch = sequence[0];
+                               chp = &sequence[1];
+                       }
                }
 
                /* Slash and NULL are not permitted */
@@ -255,8 +314,8 @@ utf8_decodestr(const u_int8_t* utf8p, size_t utf8len, u_int16_t* ucsp,
 {
        u_int16_t* bufstart;
        u_int16_t* bufend;
-       u_int16_t ucs_ch;
-       u_int8_t byte;
+       unsigned int ucs_ch;
+       unsigned int byte;
        int result = 0;
        int decompose, precompose, swapbytes;
 
@@ -273,7 +332,7 @@ utf8_decodestr(const u_int8_t* utf8p, size_t utf8len, u_int16_t* ucsp,
 
                /* check for ascii */
                if (byte < 0x80) {
-                       ucs_ch = byte;                          /* 1st byte */
+                       ucs_ch = byte;                 /* 1st byte */
                } else {
                        u_int32_t ch;
                        int extrabytes = utf_extrabytes[byte >> 3];
@@ -283,70 +342,92 @@ utf8_decodestr(const u_int8_t* utf8p, size_t utf8len, u_int16_t* ucsp,
                        utf8len -= extrabytes;
 
                        switch (extrabytes) {
-                       case 1: ch = byte;                      /* 1st byte */
-                                       ch <<= 6;
-                               ch += *utf8p++;         /* 2nd byte */
-                                       ch -= 0x00003080UL;
-                                       if (ch < 0x0080)
-                                               goto invalid;
-                                       ucs_ch = ch;
+                       case 1:
+                               ch = byte; ch <<= 6;   /* 1st byte */
+                               byte = *utf8p++;       /* 2nd byte */
+                               if ((byte >> 6) != 2)
+                                       goto invalid;
+                               ch += byte;
+                               ch -= 0x00003080UL;
+                               if (ch < 0x0080)
+                                       goto invalid;
+                               ucs_ch = ch;
                                break;
-
-                       case 2: ch = byte;                      /* 1st byte */
-                                       ch <<= 6;
-                                       ch += *utf8p++;         /* 2nd byte */
-                                       ch <<= 6;
-                                       ch += *utf8p++;         /* 3rd byte */
-                                       ch -= 0x000E2080UL;
-                                       if (ch < 0x0800)
+                       case 2:
+                               ch = byte; ch <<= 6;   /* 1st byte */
+                               byte = *utf8p++;       /* 2nd byte */
+                               if ((byte >> 6) != 2)
+                                       goto invalid;
+                               ch += byte; ch <<= 6;
+                               byte = *utf8p++;       /* 3rd byte */
+                               if ((byte >> 6) != 2)
+                                       goto invalid;
+                               ch += byte;
+                               ch -= 0x000E2080UL;
+                               if (ch < 0x0800)
+                                       goto invalid;
+                               if (ch >= 0xD800) {
+                                       if (ch <= 0xDFFF)
+                                               goto invalid;
+                                       if (ch == 0xFFFE || ch == 0xFFFF)
                                                goto invalid;
-                                       ucs_ch = ch;
-                                       break;
-
-                       case 3: ch = byte;                      /* 1st byte */
-                                       ch <<= 6;
-                                       ch += *utf8p++;         /* 2nd byte */
-                                       ch <<= 6;
-                                       ch += *utf8p++;         /* 3rd byte */
-                                       ch <<= 6;
-                               ch += *utf8p++;         /* 4th byte */
-                                       ch -= 0x03C82080UL + SP_HALF_BASE;
-                                       ucs_ch = (ch >> SP_HALF_SHIFT) + SP_HIGH_FIRST;
-                                       *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
-                                       if (ucsp >= bufend)
-                                               goto toolong;
-                                       ucs_ch = (ch & SP_HALF_MASK) + SP_LOW_FIRST;
-                                       *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
+                               }
+                               ucs_ch = ch;
+                               break;
+                       case 3:
+                               ch = byte; ch <<= 6;   /* 1st byte */
+                               byte = *utf8p++;       /* 2nd byte */
+                               if ((byte >> 6) != 2)
+                                       goto invalid;
+                               ch += byte; ch <<= 6;
+                               byte = *utf8p++;       /* 3rd byte */
+                               if ((byte >> 6) != 2)
+                                       goto invalid;
+                               ch += byte; ch <<= 6;
+                               byte = *utf8p++;       /* 4th byte */
+                               if ((byte >> 6) != 2)
+                                       goto invalid;
+                               ch += byte;
+                               ch -= 0x03C82080UL + SP_HALF_BASE;
+                               ucs_ch = (ch >> SP_HALF_SHIFT) + SP_HIGH_FIRST;
+                               if (ucs_ch < SP_HIGH_FIRST || ucs_ch > SP_HIGH_LAST)
+                                       goto invalid;
+                               *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
+                               if (ucsp >= bufend)
+                                       goto toolong;
+                               ucs_ch = (ch & SP_HALF_MASK) + SP_LOW_FIRST;
+                               if (ucs_ch < SP_LOW_FIRST || ucs_ch > SP_LOW_LAST)
+                                       goto invalid;
+                               *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
                                continue;
-
                        default:
-                                       goto invalid;
+                               goto invalid;
                        }
                        if (decompose) {
-                               u_int16_t comb_ch[2];
+                               if (unicode_decomposeable(ucs_ch)) {
+                                       u_int16_t sequence[8];
+                                       int count, i;
 
-                               ucs_ch = ucs_decompose(ucs_ch, &comb_ch[0]);
+                                       count = unicode_decompose(ucs_ch, sequence);
 
-                               if (comb_ch[0]) {
-                                       *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
-                                       if (ucsp >= bufend)
-                                               goto toolong;
-                                       ucs_ch = comb_ch[0];
-                                       if (comb_ch[1]) {
+                                       for (i = 0; i < count; ++i) {
+                                               ucs_ch = sequence[i];
                                                *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
                                                if (ucsp >= bufend)
                                                        goto toolong;
-                                               ucs_ch = comb_ch[1];
                                        }
+                                       continue;                       
                                }
                        } else if (precompose && (ucsp != bufstart)) {
                                u_int16_t composite, base;
 
-                               base = swapbytes ? NXSwapShort(*(ucsp - 1)) : *(ucsp - 1);
-                               composite = ucs_combine(base, ucs_ch);
-                               if (composite) {
-                                       --ucsp;
-                                       ucs_ch = composite;
+                               if (unicode_combinable(ucs_ch)) {
+                                       base = swapbytes ? NXSwapShort(*(ucsp - 1)) : *(ucsp - 1);
+                                       composite = unicode_combine(base, ucs_ch);
+                                       if (composite) {
+                                               --ucsp;
+                                               ucs_ch = composite;
+                                       }
                                }
                        }
                        if (ucs_ch == UCS_ALT_NULL)
@@ -374,556 +455,277 @@ toolong:
 
 
 /*
- * Lookup tables for Unicode chars 0x00C0 thru 0x00FF
- * primary_char yields first decomposed char. If this
- * char is an alpha char then get the combining char
- * from the combining_char table and add 0x0300 to it.
+ * utf8_validatestr - Check for a valid UTF-8 string.
  */
+int
+utf8_validatestr(const u_int8_t* utf8p, size_t utf8len)
+{
+       unsigned int byte;
+       u_int32_t ch;
+       unsigned int ucs_ch;
+       size_t extrabytes;
 
-static unsigned char primary_char[8*36] = {
-       0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x43,
-
-       0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,                 /* CF */
-
-       0x00, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x4F, 0x00,
-
-       0x00, 0x55, 0x55, 0x55, 0x55, 0x59, 0x00, 0x00,                 /* DF */
-
-       0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00, 0x63,
-
-       0x65, 0x65, 0x65, 0x65, 0x69, 0x69, 0x69, 0x69,                 /* EF */
-
-       0x00, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x00,
-
-       0x00, 0x75, 0x75, 0x75, 0x75, 0x79, 0x00, 0x79,                 /* FF */
-
-       0x41, 0x61, 0x41, 0x61, 0x41, 0x61, 0x43, 0x63,
-       
-       0x43, 0x63, 0x43, 0x63, 0x43, 0x63, 0x44, 0x64,                 /* 10F */
-       
-       0x00, 0x00, 0x45, 0x65, 0x45, 0x65, 0x45, 0x65,
-       
-       0x45, 0x65, 0x45, 0x65, 0x47, 0x67, 0x47, 0x67,                 /* 11F */
-       
-       0x47, 0x67, 0x47, 0x67, 0x48, 0x68, 0x00, 0x00, 
-       
-       0x49, 0x69, 0x49, 0x69, 0x49, 0x69, 0x49, 0x69, 
-       
-       0x49, 0x00, 0x00, 0x00, 0x4A, 0x6A, 0x4B, 0x6B, 
-       
-       0x00, 0x4C, 0x6C, 0x4C, 0x6C, 0x4C, 0x6C, 0x00,                 /* 13F */
-       
-       0x00, 0x00, 0x00, 0x4E, 0x6E, 0x4E, 0x6E, 0x4E, 
-       
-       0x6E, 0x00, 0x00, 0x00, 0x4F, 0x6F, 0x4F, 0x6F, 
-       
-       0x4F, 0x6F, 0x00, 0x00, 0x52, 0x72, 0x52, 0x72, 
-       
-       0x52, 0x72, 0x53, 0x73, 0x53, 0x73, 0x53, 0x73,                 /* 15F */
-       
-       0x53, 0x73, 0x54, 0x74, 0x54, 0x74, 0x00, 0x00, 
-       
-       0x55, 0x75, 0x55, 0x75, 0x55, 0x75, 0x55, 0x75, 
-       
-       0x55, 0x75, 0x55, 0x75, 0x57, 0x77, 0x59, 0x79, 
-       
-       0x59, 0x5A, 0x7A, 0x5A, 0x7A, 0x5A, 0x7A, 0x00,                 /* 17F */
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,                 /* 19F */
-       
-       0x4F, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 
-       
-       0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,                 /* 1BF */
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
-       
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x61, 0x49,                 
-       
-       0x69, 0x4F, 0x6F, 0x55, 0x75, 0xDC, 0xFC, 0xDC, 
-       
-       0xFC, 0xDC, 0xFC, 0xDC, 0xFC, 0x00, 0xC4, 0xE4                  /* 1DF */
-       
-};
-
-static unsigned char combining_char[8*36] = {
-       0x00, 0x01, 0x02, 0x03, 0x08, 0x0A, 0xFF, 0x27,
-
-       0x00, 0x01, 0x02, 0x08, 0x00, 0x01, 0x02, 0x08,                 /* CF */
-
-       0xFF, 0x03, 0x00, 0x01, 0x02, 0x03, 0x08, 0xFF,
-
-       0xFF, 0x00, 0x01, 0x02, 0x08, 0x01, 0xFF, 0xFF,                 /* DF */
-
-       0x00, 0x01, 0x02, 0x03, 0x08, 0x0A, 0xFF, 0x27,
-
-       0x00, 0x01, 0x02, 0x08, 0x00, 0x01, 0x02, 0x08,                 /* EF */
-
-       0xFF, 0x03, 0x00, 0x01, 0x02, 0x03, 0x08, 0xFF,
-
-       0xFF, 0x00, 0x01, 0x02, 0x08, 0x01, 0xFF, 0x08,                 /* FF */
-
-       0x04, 0x04, 0x06, 0x06, 0x28, 0x28, 0x01, 0x01, 
-
-       0x02, 0x02, 0x07, 0x07, 0x0C, 0x0C, 0x0C, 0x0C, 
-
-       0x00, 0x00, 0x04, 0x04, 0x06, 0x06, 0x07, 0x07, 
-
-       0x28, 0x28, 0x0C, 0x0C, 0x02, 0x02, 0x06, 0x06, 
-
-       0x07, 0x07, 0x27, 0x27, 0x02, 0x02, 0x00, 0x00, 
-
-       0x03, 0x03, 0x04, 0x04, 0x06, 0x06, 0x28, 0x28, 
-
-       0x07, 0x00, 0x00, 0x00, 0x02, 0x02, 0x27, 0x27, 
-
-       0x00, 0x01, 0x01, 0x27, 0x27, 0x0C, 0x0C, 0x00,                 /* 13F */
-
-       0x00, 0x00, 0x00, 0x01, 0x01, 0x27, 0x27, 0x0C, 
-
-       0x0C, 0x00, 0x00, 0x00, 0x04, 0x04, 0x06, 0x06, 
-
-       0x0B, 0x0B, 0x00, 0x00, 0x01, 0x01, 0x27, 0x27, 
-
-       0x0C, 0x0C, 0x01, 0x01, 0x02, 0x02, 0x27, 0x27, 
-
-       0x0C, 0x0C, 0x27, 0x27, 0x0C, 0x0C, 0x00, 0x00, 
-
-       0x03, 0x03, 0x04, 0x04, 0x06, 0x06, 0x0A, 0x0A,                 /* 16F */
-
-       0x0B, 0x0B, 0x28, 0x28, 0x02, 0x02, 0x02, 0x02, 
+       while (utf8len-- > 0 && (byte = *utf8p++) != '\0') {
+               if (byte < 0x80)
+                       continue;  /* plain ascii */
 
-       0x08, 0x01, 0x01, 0x07, 0x07, 0x0C, 0x0C, 0x00, 
+               extrabytes = utf_extrabytes[byte >> 3];
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,                 /* 17F */ 
+               if (utf8len < extrabytes)
+                       goto invalid;
+               utf8len -= extrabytes;
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+               switch (extrabytes) {
+               case 1:
+                       ch = byte; ch <<= 6;   /* 1st byte */
+                       byte = *utf8p++;       /* 2nd byte */
+                       if ((byte >> 6) != 2)
+                               goto invalid;
+                       ch += byte;
+                       ch -= 0x00003080UL;
+                       if (ch < 0x0080)
+                               goto invalid;
+                       break;
+               case 2:
+                       ch = byte; ch <<= 6;   /* 1st byte */
+                       byte = *utf8p++;       /* 2nd byte */
+                       if ((byte >> 6) != 2)
+                               goto invalid;
+                       ch += byte; ch <<= 6;
+                       byte = *utf8p++;       /* 3rd byte */
+                       if ((byte >> 6) != 2)
+                               goto invalid;
+                       ch += byte;
+                       ch -= 0x000E2080UL;
+                       if (ch < 0x0800)
+                               goto invalid;
+                       if (ch >= 0xD800) {
+                               if (ch <= 0xDFFF)
+                                       goto invalid;
+                               if (ch == 0xFFFE || ch == 0xFFFF)
+                                       goto invalid;
+                       }
+                       break;
+               case 3:
+                       ch = byte; ch <<= 6;   /* 1st byte */
+                       byte = *utf8p++;       /* 2nd byte */
+                       if ((byte >> 6) != 2)
+                               goto invalid;
+                       ch += byte; ch <<= 6;
+                       byte = *utf8p++;       /* 3rd byte */
+                       if ((byte >> 6) != 2)
+                               goto invalid;
+                       ch += byte; ch <<= 6;
+                       byte = *utf8p++;       /* 4th byte */
+                       if ((byte >> 6) != 2)
+                               goto invalid;
+                       ch += byte;
+                       ch -= 0x03C82080UL + SP_HALF_BASE;
+                       ucs_ch = (ch >> SP_HALF_SHIFT) + SP_HIGH_FIRST;
+                       if (ucs_ch < SP_HIGH_FIRST || ucs_ch > SP_HIGH_LAST)
+                               goto invalid;
+                       ucs_ch = (ch & SP_HALF_MASK) + SP_LOW_FIRST;
+                       if (ucs_ch < SP_LOW_FIRST || ucs_ch > SP_LOW_LAST)
+                               goto invalid;
+                       break;
+               default:
+                       goto invalid;
+               }
+               
+       }
+       return (0);
+invalid:
+       return (EINVAL);
+}
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,                 /* 19F */
+ /*
+  * Unicode 3.2 decomposition code (derived from Core Foundation)
+  */
 
-       0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+typedef struct {
+       u_int32_t _key;
+       u_int32_t _value;
+} unicode_mappings32;
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 
+static inline u_int32_t
+getmappedvalue32(const unicode_mappings32 *theTable, u_int32_t numElem,
+               u_int16_t character)
+{
+       const unicode_mappings32 *p, *q, *divider;
 
-       0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+       if ((character < theTable[0]._key) || (character > theTable[numElem-1]._key))
+               return (0);
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+       p = theTable;
+       q = p + (numElem-1);
+       while (p <= q) {
+               divider = p + ((q - p) >> 1);   /* divide by 2 */
+               if (character < divider->_key) { q = divider - 1; }
+               else if (character > divider->_key) { p = divider + 1; }
+               else { return (divider->_value); }
+       }
+       return (0);
+}
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+#define RECURSIVE_DECOMPOSITION        (1 << 15)
+#define EXTRACT_COUNT(value)   (((value) >> 12) & 0x0007)
 
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x0C,                 /* 1CF */
+typedef struct {
+       u_int16_t _key;
+       u_int16_t _value;
+} unicode_mappings16;
 
-       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x04, 0x04, 0x01, 
+static inline u_int16_t
+getmappedvalue16(const unicode_mappings16 *theTable, u_int32_t numElem,
+               u_int16_t character)
+{
+       const unicode_mappings16 *p, *q, *divider;
 
-       0x01, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x04, 0x04                  /* 1DF */
-};
+       if ((character < theTable[0]._key) || (character > theTable[numElem-1]._key))
+               return (0);
 
+       p = theTable;
+       q = p + (numElem-1);
+       while (p <= q) {
+               divider = p + ((q - p) >> 1);   /* divide by 2 */
+               if (character < divider->_key)
+                       q = divider - 1;
+               else if (character > divider->_key)
+                       p = divider + 1;
+               else
+                       return (divider->_value);
+       }
+       return (0);
+}
 
-/* CYRILLIC codepoints 0x0400 ~ 0x04FF */
-static const unsigned long __CyrillicDecompBitmap[] = {
-    0x510A0040, 0x00000040, 0x0000510A, 0x00000000,    /* 0x0400 */
-    0x00000000, 0x00000000, 0x00000000, 0x00000000,    /* 0x0480 */
-};
 
-/* CJK codepoints 0x3000 ~ 0x30FF */
-static const unsigned long __CJKDecompBitmap[] = {
-    0x00000000, 0x00000000, 0x000AAAAA, 0xA540DB6C,    /* 0x3000 */
-    0x00000802, 0x000AAAAA, 0xA540DB6C, 0x000009E2,    /* 0x3080 */
-};
-#define IS_DECOMPOSABLE(table,unicodeVal) \
-       (table[(unicodeVal) / 32] & (1 << (31 - ((unicodeVal) % 32))))
+static u_int32_t
+unicode_recursive_decompose(u_int16_t character, u_int16_t *convertedChars)
+{
+       u_int16_t value;
+       u_int32_t length;
+       u_int16_t firstChar;
+       u_int16_t theChar;
+       const u_int16_t *bmpMappings;
+       u_int32_t usedLength;
+
+       value = getmappedvalue16(
+               (const unicode_mappings16 *)__CFUniCharDecompositionTable,
+               __UniCharDecompositionTableLength, character);
+       length = EXTRACT_COUNT(value);
+       firstChar = value & 0x0FFF;
+       theChar = firstChar;
+       bmpMappings = (length == 1 ? &theChar : __CFUniCharMultipleDecompositionTable + firstChar);
+       usedLength = 0;
+
+       if (value & RECURSIVE_DECOMPOSITION) {
+           usedLength = unicode_recursive_decompose((u_int16_t)*bmpMappings, convertedChars);
+       
+           --length;   /* Decrement for the first char */
+           if (!usedLength)
+               return 0;
+           ++bmpMappings;
+           convertedChars += usedLength;
+       }
+       
+       usedLength += length;
+       
+       while (length--)
+               *(convertedChars++) = *(bmpMappings++);
+       
+       return (usedLength);
+}
+    
+#define HANGUL_SBASE 0xAC00
+#define HANGUL_LBASE 0x1100
+#define HANGUL_VBASE 0x1161
+#define HANGUL_TBASE 0x11A7
+
+#define HANGUL_SCOUNT 11172
+#define HANGUL_LCOUNT 19
+#define HANGUL_VCOUNT 21
+#define HANGUL_TCOUNT 28
+#define HANGUL_NCOUNT (HANGUL_VCOUNT * HANGUL_TCOUNT)
 
 /*
- * ucs_decompose - decompose a composed Unicode char
+ * unicode_decompose - decompose a composed Unicode char
  *
  * Composed Unicode characters are forbidden on
  * HFS Plus volumes. ucs_decompose will convert a
  * composed character into its correct decomposed
  * sequence.
  *
- * Currently only Tier-1 and Tier-2 languages
- * are handled.  Other composed characters are
- * passed unchanged.
+ * Similar to CFUniCharDecomposeCharacter
  */
-static u_int16_t
-ucs_decompose(register u_int16_t ch, u_int16_t *cmb)
+static int
+unicode_decompose(u_int16_t character, u_int16_t *convertedChars)
 {
-       u_int16_t base;
-       
-       cmb[0] = 0;
-       cmb[1] = 0;
-
-       if (ch < 0x00C0) {
-               base = ch;
-       } else if (ch <= 0x01DF) {
-               
-               base = (u_int16_t) primary_char[ch - 0x00C0];
-
-               if (base == 0)
-                       base = ch;
-               else  {
-                   if ((base < 0x00C0) || (primary_char[base - 0x00C0] == 0))
-                           cmb[0] = (u_int16_t)0x0300 + (u_int16_t)combining_char[ch - 0x00C0];
-                       else {
-                       u_int16_t   tch = base;
-                       
-                       base    = (u_int16_t)primary_char[tch - 0x00C0];
-                           cmb[0]  = (u_int16_t)0x0300 + (u_int16_t)combining_char[tch - 0x00C0];
-                           cmb[1]  = (u_int16_t)0x0300 + (u_int16_t)combining_char[ch - 0x00C0];
-                       }
-               }
-       } else if ((ch >= 0x0400) && (ch <= 0x04FF) &&
-                  IS_DECOMPOSABLE(__CyrillicDecompBitmap, ch - 0x0400)) {
-       
-               /* Handle CYRILLIC LETTERs */
-               switch(ch) {
-               case 0x0401: base = 0x0415; cmb[0] = 0x0308; break; /*  */
-               case 0x0403: base = 0x0413; cmb[0] = 0x0301; break; /*  */
-               case 0x0407: base = 0x0406; cmb[0] = 0x0308; break; /*  */
-               case 0x040C: base = 0x041A; cmb[0] = 0x0301; break; /*  */
-               case 0x040E: base = 0x0423; cmb[0] = 0x0306; break; /*  */
-               case 0x0419: base = 0x0418; cmb[0] = 0x0306; break; /*  */
-               case 0x0439: base = 0x0438; cmb[0] = 0x0306; break; /*  */
-               case 0x0451: base = 0x0435; cmb[0] = 0x0308; break; /*  */
-               case 0x0453: base = 0x0433; cmb[0] = 0x0301; break; /*  */
-               case 0x0457: base = 0x0456; cmb[0] = 0x0308; break; /*  */
-               case 0x045C: base = 0x043A; cmb[0] = 0x0301; break; /*  */
-               case 0x045E: base = 0x0443; cmb[0] = 0x0306; break; /*  */
-               
-               default:
-                       /* Should not be hit from bit map table */
-                       base = ch;
-               }
-       } else if (ch == 0x1E3F) {
-               base = 0x006D; cmb[0] = 0x0301; /* LATIN SMALL LETTER M WITH ACUTE */
-       } else if ((ch > 0x3000) && (ch < 0x3100) &&
-                  IS_DECOMPOSABLE(__CJKDecompBitmap, ch - 0x3000)) {
-       
-               /* Handle HIRAGANA LETTERs */
-               switch(ch) {
-               case 0x3071: base = 0x306F; cmb[0] = 0x309A; break; /* PA */
-               case 0x3074: base = 0x3072; cmb[0] = 0x309A; break; /* PI */
-               case 0x3077: base = 0x3075; cmb[0] = 0x309A; break; /* PU */
-               case 0x307A: base = 0x3078; cmb[0] = 0x309A; break; /* PE */
-
-               case 0x307D: base = 0x307B; cmb[0] = 0x309A; break; /* PO */
-               case 0x3094: base = 0x3046; cmb[0] = 0x3099; break; /* VU */
-               case 0x30D1: base = 0x30CF; cmb[0] = 0x309A; break; /* PA */
-               case 0x30D4: base = 0x30D2; cmb[0] = 0x309A; break; /* PI */
-
-               case 0x30D7: base = 0x30D5; cmb[0] = 0x309A; break; /* PU */
-               case 0x30DA: base = 0x30D8; cmb[0] = 0x309A; break; /* PE */
-               case 0x30DD: base = 0x30DB; cmb[0] = 0x309A; break; /* PO */
-               case 0x30F4: base = 0x30A6; cmb[0] = 0x3099; break; /* VU */
-
-               case 0x30F7: base = 0x30EF; cmb[0] = 0x3099; break; /* VA */
-               case 0x30F8: base = 0x30F0; cmb[0] = 0x3099; break; /* VI */
-               case 0x30F9: base = 0x30F1; cmb[0] = 0x3099; break; /* VE */
-               case 0x30FA: base = 0x30F2; cmb[0] = 0x3099; break; /* VO */
-               
-               default:
-                       /* the rest (41 of them) have a simple conversion */
-                       base = ch - 1;
-                       cmb[0] = 0x3099;
-               }
-       } else if ((ch >= 0xAC00) && (ch < 0xD7A4)) {
-               /* Hangul */
-               ch -= 0xAC00;
-               base    = 0x1100 + (ch / (21*28));
-               cmb[0]  = 0x1161 + (ch % (21*28)) / 28;
-
-               if (ch % 28)
-                       cmb[1] = 0x11A7 + (ch % 28);
+       if ((character >= HANGUL_SBASE) &&
+           (character <= (HANGUL_SBASE + HANGUL_SCOUNT))) {
+               u_int32_t length;
+
+               character -= HANGUL_SBASE;
+               length = (character % HANGUL_TCOUNT ? 3 : 2);
+
+               *(convertedChars++) =
+                       character / HANGUL_NCOUNT + HANGUL_LBASE;
+               *(convertedChars++) =
+                       (character % HANGUL_NCOUNT) / HANGUL_TCOUNT + HANGUL_VBASE;
+               if (length > 2)
+                       *convertedChars = (character % HANGUL_TCOUNT) + HANGUL_TBASE;
+               return (length);
        } else {
-               base = ch;
+               return (unicode_recursive_decompose(character, convertedChars));
        }
-       
-       return (base);
 }
 
-
-static const short diacrit_tbl[8*6] = {
- /* 300 - 307 */     0,  58, 116, 174, 232,  -1, 290, 348,
- /* 308 - 30F */   406,  -1, 464, 522, 580,  -1,  -1,  -1,
- /* 310 - 317 */    -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,
- /* 318 - 31F */    -1,  -1,  -1, 638,  -1,  -1,  -1,  -1,
- /* 320 - 327 */    -1,  -1,  -1,  -1,  -1,  -1,  -1, 696,
- /* 328 - 32F */   754,  -1,  -1,  -1,  -1,  -1,  -1,  -1
-};
-
-static const u_int16_t composite_tbl[58*14] = {
- /*
-  *    A     B     C     D     E     F     G     H     I     J     K     L     M
-  *    N     O     P     Q     R     S     T     U     V     W     X     Y     Z
-  *    [     \     ]     ^     _     `
-  *    a     b     c     d     e     f     g     h     i     j     k     l     m
-  *    n     o     p     q     r     s     t     u     v     w     x     y     z
-  */
-
- /*
-  * 0x300 - grave accent
-  */
-  0x0C0,    0,    0,    0,0x0C8,    0,    0,    0,0x0CC,    0,    0,    0,    0,
-      0,0x0D2,    0,    0,    0,    0,    0,0x0D9,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,
-  0x0E0,    0,    0,    0,0x0E8,    0,    0,    0,0x0EC,    0,    0,    0,    0,
-      0,0x0F2,    0,    0,    0,    0,    0,0x0F9,    0,    0,    0,    0,    0,
- /*
-  * 0x301 - acute accent
-  */
-  0x0C1,    0,0x106,    0,0x0C9,    0,    0,    0,0x0CD,    0,    0,0x139,    0,
-  0x143,0x0D3,    0,    0,0x154,0x15A,    0,0x0DA,    0,    0,    0,0x0DD,0x179,
-      0,    0,    0,    0,    0,    0,
-  0x0E1,    0,0x107,    0,0x0E9,    0,    0,    0,0x0ED,    0,    0,0x13A,0x1E3F,
-  0x144,0x0F3,    0,    0,0x155,0x15B,    0,0x0FA,    0,    0,    0,0x0FD,0x17A,
- /*
-  * 0x302 - circumflex accent
-  */
-  0x0C2,    0,0x108,    0,0x0CA,    0,0x11C,0x124,0x0CE,0x134,    0,    0,    0,
-      0,0x0D4,    0,    0,    0,0x15C,    0,0x0DB,    0,0x174,    0,0x176,    0,
-      0,    0,    0,    0,    0,    0,
-  0x0E2,    0,0x109,    0,0x0EA,    0,0x11D,0x125,0x0EE,0x135,    0,    0,    0,
-      0,0x0F4,    0,    0,    0,0x15D,    0,0x0FB,    0,0x175,    0,0x177,    0,
- /*
-  * 0x303 - tilde
-  */
-  0x0C3,    0,    0,    0,    0,    0,    0,    0,0x128,    0,    0,    0,    0,
-  0x0D1,0x0D5,    0,    0,    0,    0,    0,0x168,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,
-  0x0E3,    0,    0,    0,    0,    0,    0,    0,0x129,    0,    0,    0,    0,
-  0x0F1,0x0F5,    0,    0,    0,    0,    0,0x169,    0,    0,    0,    0,    0,
- /*
-  * 0x304 - macron
-  */
-  0x100,    0,    0,    0,0x112,    0,    0,    0,0x12A,    0,    0,    0,    0,
-      0,0x14C,    0,    0,    0,    0,    0,0x16A,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,
-  0x101,    0,    0,    0,0x113,    0,    0,    0,0x12B,    0,    0,    0,    0,
-      0,0x14D,    0,    0,    0,    0,    0,0x16B,    0,    0,    0,    0,    0,
- /*
-  * 0x306 - breve
-  */
-  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,    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,
- /*
-  * 0x307 - dot above
-  */
-      0,    0,0x10A,    0,0x116,    0,0x120,    0,0x130,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,0x17B,
-      0,    0,    0,    0,    0,    0,
-      0,    0,0x10B,    0,0x117,    0,0x121,    0,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,0x17C,
- /*
-  * 0x308 - diaeresis
-  */
-  0x0C4,    0,    0,    0,0x0CB,    0,    0,    0,0x0CF,    0,    0,    0,    0,
-      0,0x0D6,    0,    0,    0,    0,    0,0x0DC,    0,    0,    0,0x178,    0,
-      0,    0,    0,    0,    0,    0,
-  0x0E4,    0,    0,    0,0x0EB,    0,    0,    0,0x0EF,    0,    0,    0,    0,
-      0,0x0F6,    0,    0,    0,    0,    0,0x0FC,    0,    0,    0,0x0FF,    0,
- /*
-  * 0x30A - ring above
-  */
-  0x0C5,    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,    0,
-  0x0E5,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,    0,0x16F,    0,    0,    0,    0,    0,
- /*
-  * 0x30B - double aute accent
-  */
-      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,    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,
- /*
-  * 0x30C - caron
-  */
-  0x1CD,    0,0x10C,0x10E,0x11A,    0,    0,    0,0x1CF,    0,    0,0x13D,    0,
-  0x147,0x1D1,    0,    0,0x158,0x160,0x164,0x1D3,    0,    0,    0,    0,0x17D,
-      0,    0,    0,    0,    0,    0,
-  0x1CE,    0,0x10D,0x10F,0x11B,    0,    0,    0,0x1D0,    0,    0,0x13E,    0,
-  0x148,0x1D2,    0,    0,0x159,0x161,0x165,0x1D4,    0,    0,    0,    0,0x17E,
- /*
-  * 0x31B - horn
-  */
-      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-      0,0x1A0,    0,    0,    0,    0,    0,0x1AF,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-      0,0x1A1,    0,    0,    0,    0,    0,0x1B0,    0,    0,    0,    0,    0,
- /*
-  * 0x327 - cedilla
-  */
-      0,    0,0x0C7,    0,    0,    0,0x122,    0,    0,    0,0x136,0x13B,    0,
-  0x145,    0,    0,    0,0x156,0x15E,0x162,    0,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,
-      0,    0,0x0E7,    0,    0,    0,0x123,    0,    0,    0,0x137,0x13C,    0,
-  0x146,    0,    0,    0,0x157,0x15F,0x163,    0,    0,    0,    0,    0,    0,
- /*
-  * 0x328 - ogonek
-  */
-  0x104,    0,    0,    0,0x118,    0,    0,    0,0x12E,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,    0,0x172,    0,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,
-  0x105,    0,    0,    0,0x119,    0,    0,    0,0x12F,    0,    0,    0,    0,
-      0,    0,    0,    0,    0,    0,    0,0x173,    0,    0,    0,    0,    0,
-};
-
-
-/* CJK codepoints 0x3000 ~ 0x30FF */
-static const unsigned long __CJKCombBitmap[] = {
-       0x00000000, 0x00000000, 0x02155555, 0x4A812490, /* 0x3000 */
-       0x00000004, 0x02155555, 0x4A812490, 0x0001E004, /* 0x3080 */
-};
-#define CAN_COMBINE(table,unicodeVal) \
-       (table[(unicodeVal) / 32] & (1 << (31 - ((unicodeVal) % 32))))
-
-
 /*
- * ucs_combine - generate a precomposed Unicode char
+ * unicode_combine - generate a precomposed Unicode char
  *
  * Precomposed Unicode characters are required for some volume
- * formats and network protocols.  ucs_combine will combine a
- * decomposed character sequence into a single precomposed
+ * formats and network protocols.  unicode_combine will combine
+ * decomposed character sequence into a single precomposed
  * (composite) character.
  *
- * Currently only decomcomposed sequences from Apple's Tier 1
- * and Tier 2 languages are handled.
- *
- * INPUT:
- *             base - base character
- *             comb - combining character
- * OUTPUT:
- *             result - precomposed char or zero if not combinable
+ * Similar toCFUniCharPrecomposeCharacter but unicode_combine
+ * also handles Hangul Jamo characters.
  */
 static u_int16_t
-ucs_combine(u_int16_t base, u_int16_t comb)
+unicode_combine(u_int16_t base, u_int16_t combining)
 {
-       /* Get out early if we can */
-       if (comb < 0x0300)
-               return (0);
-
-       /* Try ordinary diacritics (0x300 - 0x32F) */
-       if (comb <= 0x032F) {
-               int index;
-               
-               if (base >= 'A' && base <= 'z') {
-                       index = diacrit_tbl[comb - 0x0300];
-                       if (index < 0 ) return (0);
-       
-                       return (composite_tbl[index + (base - 'A')]);
+       u_int32_t value;
+
+       /* Check HANGUL */
+       if ((combining >= HANGUL_VBASE) && (combining < (HANGUL_TBASE + HANGUL_TCOUNT))) {
+               /* 2 char Hangul sequences */
+               if ((combining < (HANGUL_VBASE + HANGUL_VCOUNT)) &&
+                   (base >= HANGUL_LBASE && base < (HANGUL_LBASE + HANGUL_LCOUNT))) {
+                   return (HANGUL_SBASE +
+                           ((base - HANGUL_LBASE)*(HANGUL_VCOUNT*HANGUL_TCOUNT)) +
+                           ((combining  - HANGUL_VBASE)*HANGUL_TCOUNT));
                }
-
-               /* Handle Cyrillic and some 3 char latin sequences */
-               switch (comb) {
-               case 0x0300:
-                       switch (base) {
-                       case 0x00DC:  return (0x01DB);
-                       case 0x00FC:  return (0x01DC);
-                       } break;
-               case 0x0301:
-                       switch (base) {
-                       case 0x00DC:  return (0x01D7);
-                       case 0x00FC:  return (0x01D8);
-                       case 0x0413:  return (0x0403);
-                       case 0x041A:  return (0x040C);
-                       case 0x0433:  return (0x0453);
-                       case 0x043A:  return (0x045C);
-                       } break;
-               case 0x0304:
-                       switch (base) {
-                       case 0x00DC:  return (0x01D5);
-                       case 0x00FC:  return (0x01D6);
-                       case 0x00C4:  return (0x01DE);
-                       case 0x00E4:  return (0x01DF);
-                       } break;
-               case 0x0306:
-                       switch (base) {
-                       case 0x0418:  return (0x0419);
-                       case 0x0423:  return (0x040E);
-                       case 0x0438:  return (0x0439);
-                       case 0x0443:  return (0x045E);
-                       } break;
-               case 0x0308:
-                       switch (base) {
-                       case 0x0406:  return (0x0407);
-                       case 0x0415:  return (0x0401);
-                       case 0x0435:  return (0x0451);
-                       case 0x0456:  return (0x0457);
-                       } break;
-               case 0x030C:
-                       switch (base) {
-                       case 0x00DC:  return (0x01D9);
-                       case 0x00FC:  return (0x01DA);
-                       } break;
+       
+               /* 3 char Hangul sequences */
+               if ((combining > HANGUL_TBASE) &&
+                   (base >= HANGUL_SBASE && base < (HANGUL_SBASE + HANGUL_SCOUNT))) {
+                       if ((base - HANGUL_SBASE) % HANGUL_TCOUNT)
+                               return (0);
+                       else
+                               return (base + (combining - HANGUL_TBASE));
                }
-               return (0);
        }
 
-       /* Now try HANGUL */
-       if (comb < 0x1161)
-               return (0);
-
-       /* 2 char Hangul sequences */
-       if ((comb <= 0x1175)  && (base >= 0x1100 && base <= 0x1112))
-           return (0xAC00 + ((base - 0x1100)*(21*28)) + ((comb  - 0x1161)*28));
-
-       /* 3 char Hangul sequences */
-       if ((comb >= 0x11A8 && comb <= 0x11C2) &&
-               (base >= 0xAC00 && base <= 0xD788)) {
-               if ((base - 0xAC00) % 28)
-                       return (0);
-               else
-                       return (base + (comb - 0x11A7));
-       }
+       value = getmappedvalue32(
+               (const unicode_mappings32 *)__CFUniCharPrecompSourceTable,
+               __CFUniCharPrecompositionTableLength, combining);
 
-       /* Now try HIRAGANA and KATAKANA */
-       if ((comb == 0x3099 || comb == 0x309A) &&
-               (base > 0x3000 && base < 0x3100)   &&
-               CAN_COMBINE(__CJKCombBitmap, base - 0x3000)) {
-               if (comb == 0x309A) {
-                       switch(base) {
-                       case 0x306F:  return (0x3071);  /* PA */
-                       case 0x3072:  return (0x3074);  /* PI */
-                       case 0x3075:  return (0x3077);  /* PU */
-                       case 0x3078:  return (0x307A);  /* PE */
-                       case 0x307B:  return (0x307D);  /* PO */
-                       case 0x30CF:  return (0x30D1);  /* PA */
-                       case 0x30D2:  return (0x30D4);  /* PI */
-                       case 0x30D5:  return (0x30D7);  /* PU */
-                       case 0x30D8:  return (0x30DA);  /* PE */
-                       case 0x30DB:  return (0x30DD);  /* PO */
-                       default:      return (0);
-                       }
-               } else /* 0x3099 */ {
-                       switch (base) {
-                       case 0x3046:  return (0x3094);  /* VU */
-                       case 0x30A6:  return (0x30F4);  /* VU */
-                       case 0x30EF:  return (0x30F7);  /* VA */
-                       case 0x30F0:  return (0x30F8);  /* VI */
-                       case 0x30F1:  return (0x30F9);  /* VE */
-                       case 0x30F2:  return (0x30FA);  /* VO */
-                       default:      return (base + 1); /* 41 code points here */
-                       }
-               }
+       if (value) {
+               value = getmappedvalue16(
+                       (const unicode_mappings16 *)
+                       ((u_int32_t *)__CFUniCharBMPPrecompDestinationTable + (value & 0xFFFF)),
+                       (value >> 16), base);
        }
-
-       return (0);
+       return (value);
 }