]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_utfconv.c
xnu-792.6.76.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_utfconv.c
index d2623589b8fa2ea5bb06602d854d069938f1f6f2..11af9238adae8f323eb71baaeaac955a8d327f25 100644 (file)
@@ -117,10 +117,31 @@ unicode_decomposeable(u_int16_t character) {
        return (0);
 }
 
+
+/*
+ * Get the combing class.
+ *
+ * Similar to CFUniCharGetCombiningPropertyForCharacter.
+ */
+static inline u_int8_t
+get_combining_class(u_int16_t character) {
+       const u_int8_t *bitmap = __CFUniCharCombiningPropertyBitmap;
+
+       u_int8_t value = bitmap[(character >> 8)];
+
+       if (value) {
+               bitmap = bitmap + (value * 256);
+               return bitmap[character % 256];
+       }
+       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);
 
+static void priortysort(u_int16_t* characters, int count);
 
 char utf_extrabytes[32] = {
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
@@ -314,8 +335,9 @@ 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 combcharcnt = 0;
        int result = 0;
        int decompose, precompose, swapbytes;
 
@@ -332,7 +354,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];
@@ -342,44 +364,66 @@ 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) {
                                if (unicode_decomposeable(ucs_ch)) {
@@ -394,6 +438,7 @@ utf8_decodestr(const u_int8_t* utf8p, size_t utf8len, u_int16_t* ucsp,
                                                if (ucsp >= bufend)
                                                        goto toolong;
                                        }
+                                       combcharcnt += count - 1;
                                        continue;                       
                                }
                        } else if (precompose && (ucsp != bufstart)) {
@@ -414,8 +459,25 @@ utf8_decodestr(const u_int8_t* utf8p, size_t utf8len, u_int16_t* ucsp,
                if (ucs_ch == altslash)
                        ucs_ch = '/';
 
+               /*
+                * Make multiple combining character sequences canonical
+                */
+               if (unicode_combinable(ucs_ch)) {
+                       ++combcharcnt;   /* start tracking a run */
+               } else if (combcharcnt) {
+                       if (combcharcnt > 1) {
+                               priortysort(ucsp - combcharcnt, combcharcnt);
+                       }
+                       combcharcnt = 0;  /* start over */
+               }
                *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch;
        }
+       /*
+        * Make a previous combining sequence canonical
+        */
+       if (combcharcnt > 1) {
+               priortysort(ucsp - combcharcnt, combcharcnt);
+       }
 
 exit:
        *ucslen = (u_int8_t*)ucsp - (u_int8_t*)bufstart;
@@ -432,6 +494,91 @@ toolong:
 }
 
 
+/*
+ * 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;
+
+       while (utf8len-- > 0 && (byte = *utf8p++) != '\0') {
+               if (byte < 0x80)
+                       continue;  /* plain ascii */
+
+               extrabytes = utf_extrabytes[byte >> 3];
+
+               if (utf8len < extrabytes)
+                       goto invalid;
+               utf8len -= extrabytes;
+
+               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);
+}
+
+
  /*
   * Unicode 3.2 decomposition code (derived from Core Foundation)
   */
@@ -622,3 +769,39 @@ unicode_combine(u_int16_t base, u_int16_t combining)
        return (value);
 }
 
+
+/*
+ * priortysort - order combining chars into canonical order
+ *
+ * Similar to CFUniCharPrioritySort
+ */
+static void
+priortysort(u_int16_t* characters, int count)
+{
+       u_int32_t p1, p2;
+       u_int16_t *ch1, *ch2;
+       u_int16_t *end;
+       int changes = 1;
+
+       end = characters + count;
+       do {
+               changes = 0;
+               ch1 = characters;
+               ch2 = characters + 1;
+               p2 = get_combining_class(*ch1);
+               while (ch2 < end) {
+                       p1 = p2;
+                       p2 = get_combining_class(*ch2);
+                       if (p1 > p2) {
+                               u_int32_t tmp;
+
+                               tmp = *ch1;
+                               *ch1 = *ch2;
+                               *ch2 = tmp;
+                               changes = 1;
+                       }
+                       ++ch1;
+                       ++ch2;
+               }
+       } while (changes);
+}