X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/765c9de3b4af7c2078d16a03812ae2c7c2b24938..39236c6e673c41db228275375ab7fdb0f837b292:/bsd/vfs/vfs_utfconv.c diff --git a/bsd/vfs/vfs_utfconv.c b/bsd/vfs/vfs_utfconv.c index 462966072..f785b0d8c 100644 --- a/bsd/vfs/vfs_utfconv.c +++ b/bsd/vfs/vfs_utfconv.c @@ -1,28 +1,40 @@ /* - * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + + /* + Includes Unicode 3.2 decomposition code derived from Core Foundation */ + #include #include #include -#include +#include +#include /* * UTF-8 (Unicode Transformation Format) @@ -56,12 +68,91 @@ #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 + + +#include "vfs_utfconvdata.h" + + +/* + * Test for a combining character. + * + * Similar to __CFUniCharIsNonBaseCharacter except that + * unicode_combinable also includes Hangul Jamo characters. + */ +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. + */ +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); +} + + +/* + * 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); -static u_int16_t ucs_decompose(u_int16_t, u_int16_t *); +static u_int16_t ucs_to_sfm(u_int16_t ucs_ch, int lastchar); -static u_int16_t ucs_combine(u_int16_t base, u_int16_t comb); +static u_int16_t sfm_to_ucs(u_int16_t ucs_ch); char utf_extrabytes[32] = { @@ -69,39 +160,70 @@ char utf_extrabytes[32] = { -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 2, 2, 3, -1 }; +const char hexdigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; /* - * utf8_encodelen - Calculates the UTF-8 encoding length for a Unicode filename + * utf8_encodelen - Calculate the UTF-8 encoding length * - * NOTES: - * If '/' chars are allowed on disk then an alternate - * (replacement) char must be provided in altslash. + * This function takes a Unicode input string, ucsp, of ucslen bytes + * and calculates the size of the UTF-8 output in bytes (not including + * a NULL termination byte). The string must reside in kernel memory. * - * input flags: - * UTF_REVERSE_ENDIAN: Unicode byteorder is opposite current runtime + * If '/' chars are possible in the Unicode input then an alternate + * (replacement) char should be provided in altslash. + * + * FLAGS + * UTF_REVERSE_ENDIAN: Unicode byte order is opposite current runtime + * + * UTF_BIG_ENDIAN: Unicode byte order is always big endian + * + * UTF_LITTLE_ENDIAN: Unicode byte order is always little endian + * + * UTF_DECOMPOSED: generate fully decomposed output + * + * UTF_PRECOMPOSED is ignored since utf8_encodestr doesn't support it + * + * ERRORS + * None */ size_t -utf8_encodelen(const u_int16_t * ucsp, size_t ucslen, u_int16_t altslash, - int flags) +utf8_encodelen(const u_int16_t * ucsp, size_t ucslen, u_int16_t altslash, int flags) { u_int16_t ucs_ch; + u_int16_t * chp = NULL; + u_int16_t sequence[8]; + int extra = 0; int charcnt; int swapbytes = (flags & UTF_REVERSE_ENDIAN); + int decompose = (flags & UTF_DECOMPOSED); size_t len; - + charcnt = ucslen / 2; len = 0; while (charcnt-- > 0) { - ucs_ch = *ucsp++; - - if (swapbytes) - ucs_ch = NXSwapShort(ucs_ch); - if (ucs_ch == '/') - ucs_ch = altslash ? altslash : '_'; - else if (ucs_ch == '\0') - ucs_ch = UCS_ALT_NULL; - + if (extra > 0) { + --extra; + ucs_ch = *chp++; + } else { + ucs_ch = *ucsp++; + if (swapbytes) { + ucs_ch = OSSwapInt16(ucs_ch); + } + if (ucs_ch == '/') { + ucs_ch = altslash ? altslash : '_'; + } else if (ucs_ch == '\0') { + ucs_ch = UCS_ALT_NULL; + } else if (decompose && unicode_decomposeable(ucs_ch)) { + extra = unicode_decompose(ucs_ch, sequence) - 1; + charcnt += extra; + ucs_ch = sequence[0]; + chp = &sequence[1]; + } + } len += UNICODE_TO_UTF8_LEN(ucs_ch); } @@ -120,10 +242,18 @@ utf8_encodelen(const u_int16_t * ucsp, size_t ucslen, u_int16_t altslash, * * input flags: * UTF_REVERSE_ENDIAN: Unicode byteorder is opposite current runtime + * + * UTF_BIG_ENDIAN: Unicode byte order is always big endian + * + * UTF_LITTLE_ENDIAN: Unicode byte order is always little endian + * + * UTF_DECOMPOSED: generate fully decomposed output + * * UTF_NO_NULL_TERM: don't add NULL termination to UTF-8 output * * result: * ENAMETOOLONG: Name didn't fit; only buflen bytes were encoded + * * EINVAL: Illegal char found; char was replaced by an '_'. */ int @@ -133,13 +263,16 @@ 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); int decompose = (flags & UTF_DECOMPOSED); + int sfmconv = (flags & UTF_SFM_CONVERSIONS); int result = 0; - + bufstart = utf8p; bufend = bufstart + buflen; if (nullterm) @@ -147,19 +280,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++; + ucs_ch = swapbytes ? OSSwapInt16(*ucsp++) : *ucsp++; + + 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 */ @@ -190,13 +322,19 @@ utf8_encodestr(const u_int16_t * ucsp, size_t ucslen, u_int8_t * utf8p, *utf8p++ = 0x80 | (0x3f & ucs_ch); } else { + /* These chars never valid Unicode. */ + if (ucs_ch == 0xFFFE || ucs_ch == 0xFFFF) { + result = EINVAL; + break; + } + /* Combine valid surrogate pairs */ if (ucs_ch >= SP_HIGH_FIRST && ucs_ch <= SP_HIGH_LAST && charcnt > 0) { u_int16_t ch2; u_int32_t pair; - ch2 = swapbytes ? NXSwapShort(*ucsp) : *ucsp; + ch2 = swapbytes ? OSSwapInt16(*ucsp) : *ucsp; if (ch2 >= SP_LOW_FIRST && ch2 <= SP_LOW_LAST) { pair = ((ucs_ch - SP_HIGH_FIRST) << SP_HALF_SHIFT) + (ch2 - SP_LOW_FIRST) + SP_HALF_BASE; @@ -212,6 +350,16 @@ utf8_encodestr(const u_int16_t * ucsp, size_t ucslen, u_int8_t * utf8p, *utf8p++ = 0x80 | (0x3f & pair); continue; } + } else if (sfmconv) { + ucs_ch = sfm_to_ucs(ucs_ch); + if (ucs_ch < 0x0080) { + if (utf8p >= bufend) { + result = ENAMETOOLONG; + break; + } + *utf8p++ = ucs_ch; + continue; + } } if ((utf8p + 2) >= bufend) { result = ENAMETOOLONG; @@ -242,11 +390,21 @@ utf8_encodestr(const u_int16_t * ucsp, size_t ucslen, u_int8_t * utf8p, * (replacement) char must be provided in altslash. * * input flags: - * UTF_REV_ENDIAN: Unicode byteorder is oposite current runtime - * UTF_DECOMPOSED: Unicode output string must be fully decompsed + * UTF_REV_ENDIAN: Unicode byte order is opposite current runtime + * + * UTF_BIG_ENDIAN: Unicode byte order is always big endian + * + * UTF_LITTLE_ENDIAN: Unicode byte order is always little endian + * + * UTF_DECOMPOSED: generate fully decomposed output (NFD) + * + * UTF_PRECOMPOSED: generate precomposed output (NFC) + * + * UTF_ESCAPE_ILLEGAL: percent escape any illegal UTF-8 input * * result: * ENAMETOOLONG: Name didn't fit; only ucslen chars were decoded. + * * EINVAL: Illegal UTF-8 sequence found. */ int @@ -255,14 +413,19 @@ 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; + int decompose, precompose, swapbytes, escaping; + int sfmconv; + int extrabytes; - decompose = (flags & UTF_DECOMPOSED); + decompose = (flags & UTF_DECOMPOSED); precompose = (flags & UTF_PRECOMPOSED); - swapbytes = (flags & UTF_REVERSE_ENDIAN); + swapbytes = (flags & UTF_REVERSE_ENDIAN); + escaping = (flags & UTF_ESCAPE_ILLEGAL); + sfmconv = (flags & UTF_SFM_CONVERSIONS); bufstart = ucsp; bufend = (u_int16_t *)((u_int8_t *)ucsp + buflen); @@ -273,80 +436,115 @@ 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 = sfmconv ? ucs_to_sfm(byte, utf8len == 0) : byte; } else { u_int32_t ch; - int extrabytes = utf_extrabytes[byte >> 3]; - if (utf8len < extrabytes) - goto invalid; + extrabytes = utf_extrabytes[byte >> 3]; + if ((extrabytes < 0) || ((int)utf8len < extrabytes)) { + goto escape; + } 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 escape2; + ch += byte; + ch -= 0x00003080UL; + if (ch < 0x0080) + goto escape2; + 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) - 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; + case 2: + ch = byte; ch <<= 6; /* 1st byte */ + byte = *utf8p++; /* 2nd byte */ + if ((byte >> 6) != 2) + goto escape2; + ch += byte; ch <<= 6; + byte = *utf8p++; /* 3rd byte */ + if ((byte >> 6) != 2) + goto escape3; + ch += byte; + ch -= 0x000E2080UL; + if (ch < 0x0800) + goto escape3; + if (ch >= 0xD800) { + if (ch <= 0xDFFF) + goto escape3; + if (ch == 0xFFFE || ch == 0xFFFF) + goto escape3; + } + ucs_ch = ch; + break; + case 3: + ch = byte; ch <<= 6; /* 1st byte */ + byte = *utf8p++; /* 2nd byte */ + if ((byte >> 6) != 2) + goto escape2; + ch += byte; ch <<= 6; + byte = *utf8p++; /* 3rd byte */ + if ((byte >> 6) != 2) + goto escape3; + ch += byte; ch <<= 6; + byte = *utf8p++; /* 4th byte */ + if ((byte >> 6) != 2) + goto escape4; + 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 escape4; + *ucsp++ = swapbytes ? OSSwapInt16(ucs_ch) : (u_int16_t)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) { + --ucsp; + goto escape4; + } + *ucsp++ = swapbytes ? OSSwapInt16(ucs_ch) : (u_int16_t)ucs_ch; continue; - default: - goto invalid; + result = EINVAL; + goto exit; } if (decompose) { - u_int16_t comb_ch[2]; - - ucs_ch = ucs_decompose(ucs_ch, &comb_ch[0]); + if (unicode_decomposeable(ucs_ch)) { + u_int16_t sequence[8]; + int count, i; + + /* Before decomposing a new unicode character, sort + * previous combining characters, if any, and reset + * the counter. + */ + if (combcharcnt > 1) { + priortysort(ucsp - combcharcnt, combcharcnt); + } + combcharcnt = 0; - 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]) { - *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : ucs_ch; + count = unicode_decompose(ucs_ch, sequence); + for (i = 0; i < count; ++i) { + ucs_ch = sequence[i]; + *ucsp++ = swapbytes ? OSSwapInt16(ucs_ch) : (u_int16_t)ucs_ch; if (ucsp >= bufend) goto toolong; - ucs_ch = comb_ch[1]; } + combcharcnt += count - 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 ? OSSwapInt16(*(ucsp - 1)) : *(ucsp - 1); + composite = unicode_combine(base, ucs_ch); + if (composite) { + --ucsp; + ucs_ch = composite; + } } } if (ucs_ch == UCS_ALT_NULL) @@ -355,18 +553,68 @@ utf8_decodestr(const u_int8_t* utf8p, size_t utf8len, u_int16_t* ucsp, if (ucs_ch == altslash) ucs_ch = '/'; - *ucsp++ = swapbytes ? NXSwapShort(ucs_ch) : 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 ? OSSwapInt16(ucs_ch) : (u_int16_t)ucs_ch; + continue; + + /* + * Escape illegal UTF-8 into something legal. + */ +escape4: + utf8p -= 3; + goto escape; +escape3: + utf8p -= 2; + goto escape; +escape2: + utf8p -= 1; +escape: + if (!escaping) { + result = EINVAL; + goto exit; + } + if (extrabytes > 0) + utf8len += extrabytes; + byte = *(utf8p - 1); + if ((ucsp + 2) >= bufend) + goto toolong; + + /* Make a previous combining sequence canonical. */ + if (combcharcnt > 1) { + priortysort(ucsp - combcharcnt, combcharcnt); + } + combcharcnt = 0; + + ucs_ch = '%'; + *ucsp++ = swapbytes ? OSSwapInt16(ucs_ch) : (u_int16_t)ucs_ch; + ucs_ch = hexdigits[byte >> 4]; + *ucsp++ = swapbytes ? OSSwapInt16(ucs_ch) : (u_int16_t)ucs_ch; + ucs_ch = hexdigits[byte & 0x0F]; + *ucsp++ = swapbytes ? OSSwapInt16(ucs_ch) : (u_int16_t)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; return (result); -invalid: - result = EINVAL; - goto exit; - toolong: result = ENAMETOOLONG; goto exit; @@ -374,556 +622,543 @@ 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, + while (utf8len-- > 0 && (byte = *utf8p++) != '\0') { + if (byte < 0x80) + continue; /* plain ascii */ - 0x0B, 0x0B, 0x00, 0x00, 0x01, 0x01, 0x27, 0x27, + extrabytes = utf_extrabytes[byte >> 3]; - 0x0C, 0x0C, 0x01, 0x01, 0x02, 0x02, 0x27, 0x27, + if (utf8len < extrabytes) + goto invalid; + utf8len -= extrabytes; - 0x0C, 0x0C, 0x27, 0x27, 0x0C, 0x0C, 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); +} - 0x03, 0x03, 0x04, 0x04, 0x06, 0x06, 0x0A, 0x0A, /* 16F */ +/* + * utf8_normalizestr - Normalize a UTF-8 string (NFC or NFD) + * + * This function takes an UTF-8 input string, instr, of inlen bytes + * and produces normalized UTF-8 output into a buffer of buflen bytes + * pointed to by outstr. The size of the output in bytes (not including + * a NULL termination byte) is returned in outlen. In-place conversions + * are not supported (i.e. instr != outstr).] + + * FLAGS + * UTF_DECOMPOSED: output string will be fully decomposed (NFD) + * + * UTF_PRECOMPOSED: output string will be precomposed (NFC) + * + * UTF_NO_NULL_TERM: do not add null termination to output string + * + * UTF_ESCAPE_ILLEGAL: percent escape any illegal UTF-8 input + * + * ERRORS + * ENAMETOOLONG: output did not fit or input exceeded MAXPATHLEN bytes + * + * EINVAL: illegal UTF-8 sequence encountered or invalid flags + */ +int +utf8_normalizestr(const u_int8_t* instr, size_t inlen, u_int8_t* outstr, + size_t *outlen, size_t buflen, int flags) +{ + u_int16_t unicodebuf[32]; + u_int16_t* unistr = NULL; + size_t unicode_bytes; + size_t uft8_bytes; + size_t inbuflen; + u_int8_t *outbufstart, *outbufend; + const u_int8_t *inbufstart; + unsigned int byte; + int decompose, precompose; + int result = 0; - 0x0B, 0x0B, 0x28, 0x28, 0x02, 0x02, 0x02, 0x02, + if (flags & ~(UTF_DECOMPOSED | UTF_PRECOMPOSED | UTF_NO_NULL_TERM | UTF_ESCAPE_ILLEGAL)) { + return (EINVAL); + } + decompose = (flags & UTF_DECOMPOSED); + precompose = (flags & UTF_PRECOMPOSED); + if ((decompose && precompose) || (!decompose && !precompose)) { + return (EINVAL); + } + outbufstart = outstr; + outbufend = outbufstart + buflen; + inbufstart = instr; + inbuflen = inlen; + + while (inlen-- > 0 && (byte = *instr++) != '\0') { + if (outstr >= outbufend) { + result = ENAMETOOLONG; + goto exit; + } + if (byte >= 0x80) { + goto nonASCII; + } + /* ASCII is already normalized. */ + *outstr++ = byte; + } +exit: + *outlen = outstr - outbufstart; + if (((flags & UTF_NO_NULL_TERM) == 0)) { + if (outstr < outbufend) + *outstr++ = '\0'; + else + result = ENAMETOOLONG; + } + return (result); - 0x08, 0x01, 0x01, 0x07, 0x07, 0x0C, 0x0C, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 17F */ + /* + * Non-ASCII uses the existing utf8_encodestr/utf8_decodestr + * functions to perform the normalization. Since this will + * presumably be used to normalize filenames in the back-end + * (on disk or over-the-wire), it should be fast enough. + */ +nonASCII: - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Make sure the input size is reasonable. */ + if (inbuflen > MAXPATHLEN) { + result = ENAMETOOLONG; + goto exit; + } + /* + * Compute worst case Unicode buffer size. + * + * For pre-composed output, every UTF-8 input byte will be at + * most 2 Unicode bytes. For decomposed output, 2 UTF-8 bytes + * (smallest composite char sequence) may yield 6 Unicode bytes + * (1 base char + 2 combining chars). + */ + unicode_bytes = precompose ? (inbuflen * 2) : (inbuflen * 3); + + if (unicode_bytes <= sizeof(unicodebuf)) + unistr = &unicodebuf[0]; + else + MALLOC(unistr, u_int16_t *, unicode_bytes, M_TEMP, M_WAITOK); + + /* Normalize the string. */ + result = utf8_decodestr(inbufstart, inbuflen, unistr, &unicode_bytes, + unicode_bytes, 0, flags & ~UTF_NO_NULL_TERM); + if (result == 0) { + /* Put results back into UTF-8. */ + result = utf8_encodestr(unistr, unicode_bytes, outbufstart, + &uft8_bytes, buflen, 0, UTF_NO_NULL_TERM); + outstr = outbufstart + uft8_bytes; + } + if (unistr && unistr != &unicodebuf[0]) { + FREE(unistr, M_TEMP); + } + goto exit; +} - 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 int +unicode_decompose(u_int16_t character, u_int16_t *convertedChars) +{ + 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 { + return (unicode_recursive_decompose(character, convertedChars)); + } +} + +/* + * unicode_combine - generate a precomposed Unicode char + * + * Precomposed Unicode characters are required for some volume + * formats and network protocols. unicode_combine will combine + * a decomposed character sequence into a single precomposed + * (composite) character. + * + * Similar toCFUniCharPrecomposeCharacter but unicode_combine + * also handles Hangul Jamo characters. */ static u_int16_t -ucs_decompose(register u_int16_t ch, u_int16_t *cmb) +unicode_combine(u_int16_t base, u_int16_t combining) { - u_int16_t base; + 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)); + } - cmb[0] = 0; - cmb[1] = 0; + /* 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)); + } + } - if (ch < 0x00C0) { - base = ch; - } else if (ch <= 0x01DF) { - - base = (u_int16_t) primary_char[ch - 0x00C0]; + value = getmappedvalue32( + (const unicode_mappings32 *)__CFUniCharPrecompSourceTable, + __CFUniCharPrecompositionTableLength, combining); - 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]; + if (value) { + value = getmappedvalue16( + (const unicode_mappings16 *) + ((const u_int32_t *)__CFUniCharBMPPrecompDestinationTable + (value & 0xFFFF)), + (value >> 16), base); + } + 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 = 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; } - } 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); - } else { - base = ch; - } - - return (base); + } while (changes); } -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 -}; +/* + * 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/ + */ -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 - */ +#define MAX_SFM2MAC 0x29 +#define SFMCODE_PREFIX_MASK 0xf000 - /* - * 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, +/* + * 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 */ }; - -/* CJK codepoints 0x3000 ~ 0x30FF */ -static const unsigned long __CJKCombBitmap[] = { - 0x00000000, 0x00000000, 0x02155555, 0x4A812490, /* 0x3000 */ - 0x00000004, 0x02155555, 0x4A812490, 0x0001E004, /* 0x3080 */ +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 */ }; -#define CAN_COMBINE(table,unicodeVal) \ - (table[(unicodeVal) / 32] & (1 << (31 - ((unicodeVal) % 32)))) /* - * ucs_combine - generate a precomposed Unicode char + * Encode illegal NTFS filename characters into SFM Private Unicode characters * - * Precomposed Unicode characters are required for some volume - * formats and network protocols. ucs_combine will combine a - * 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 + * Assumes non-zero ASCII input. */ static u_int16_t -ucs_combine(u_int16_t base, u_int16_t comb) +ucs_to_sfm(u_int16_t ucs_ch, int lastchar) { - /* 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')]); - } - - /* 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; - } - return (0); + /* 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); } - - /* 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)); + /* 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); +} - /* 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 */ - } - } +/* + * 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 (0); + return (ucs_ch); } +