+
+/*
+ * 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 = 0;
+
+ 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 && p2 != 0) {
+ u_int32_t tmp;
+
+ tmp = *ch1;
+ *ch1 = *ch2;
+ *ch2 = tmp;
+ changes = 1;
+
+ /*
+ * Make sure that p2 contains the combining class for the
+ * character now stored at *ch2. This isn't required for
+ * correctness, but it will be more efficient if a character
+ * with a large combining class has to "bubble past" several
+ * characters with lower combining classes.
+ */
+ p2 = p1;
+ }
+ ++ch1;
+ ++ch2;
+ }
+ } while (changes);
+}
+
+
+/*
+ * Invalid NTFS filename characters are encodeded using the
+ * SFM (Services for Macintosh) private use Unicode characters.
+ *
+ * These should only be used for SMB, MSDOS or NTFS.
+ *
+ * Illegal NTFS Char SFM Unicode Char
+ * ----------------------------------------
+ * 0x01-0x1f 0xf001-0xf01f
+ * '"' 0xf020
+ * '*' 0xf021
+ * '/' 0xf022
+ * '<' 0xf023
+ * '>' 0xf024
+ * '?' 0xf025
+ * '\' 0xf026
+ * '|' 0xf027
+ * ' ' 0xf028 (Only if last char of the name)
+ * '.' 0xf029 (Only if last char of the name)
+ * ----------------------------------------
+ *
+ * Reference: http://support.microsoft.com/kb/q117258/
+ */
+
+#define MAX_SFM2MAC 0x29
+#define SFMCODE_PREFIX_MASK 0xf000
+
+/*
+ * In the Mac OS 9 days the colon was illegal in a file name. For that reason
+ * SFM had no conversion for the colon. There is a conversion for the
+ * slash. In Mac OS X the slash is illegal in a file name. So for us the colon
+ * is a slash and a slash is a colon. So we can just replace the slash with the
+ * colon in our tables and everything will just work.
+ */
+static u_int8_t
+sfm2mac[42] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00 - 07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08 - 0F */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10 - 17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18 - 1F */
+ 0x22, 0x2a, 0x3a, 0x3c, 0x3e, 0x3f, 0x5c, 0x7c, /* 20 - 27 */
+ 0x20, 0x2e /* 28 - 29 */
+};
+
+static u_int8_t
+mac2sfm[112] = {
+ 0x20, 0x21, 0x20, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20 - 27 */
+ 0x28, 0x29, 0x21, 0x2b, 0x2c, 0x2d, 0x2e, 0x22, /* 28 - 2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30 - 37 */
+ 0x38, 0x39, 0x22, 0x3b, 0x23, 0x3d, 0x24, 0x25, /* 38 - 3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40 - 47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48 - 4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50 - 57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x26, 0x5d, 0x5e, 0x5f, /* 58 - 5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60 - 67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68 - 6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70 - 77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x27, 0x7d, 0x7e, 0x7f /* 78 - 7f */
+};
+
+
+/*
+ * Encode illegal NTFS filename characters into SFM Private Unicode characters
+ *
+ * Assumes non-zero ASCII input.
+ */
+static u_int16_t
+ucs_to_sfm(u_int16_t ucs_ch, int lastchar)
+{
+ /* The last character of filename cannot be a space or period. */
+ if (lastchar) {
+ if (ucs_ch == 0x20)
+ return (0xf028);
+ else if (ucs_ch == 0x2e)
+ return (0xf029);
+ }
+ /* 0x01 - 0x1f is simple transformation. */
+ if (ucs_ch <= 0x1f) {
+ return (ucs_ch | 0xf000);
+ } else /* 0x20 - 0x7f */ {
+ u_int16_t lsb;
+
+ lsb = mac2sfm[ucs_ch - 0x0020];
+ if (lsb != ucs_ch)
+ return(0xf000 | lsb);
+ }
+ return (ucs_ch);
+}
+
+/*
+ * Decode any SFM Private Unicode characters
+ */
+static u_int16_t
+sfm_to_ucs(u_int16_t ucs_ch)
+{
+ if (((ucs_ch & 0xffC0) == SFMCODE_PREFIX_MASK) &&
+ ((ucs_ch & 0x003f) <= MAX_SFM2MAC)) {
+ ucs_ch = sfm2mac[ucs_ch & 0x003f];
+ }
+ return (ucs_ch);
+}
+
+