diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 689c8bd721fb..5df0052b2775 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -239,58 +239,75 @@ out: return res; } -int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len) +/* + * Convert one or more ASCII characters into a single unicode character. + * Returns the number of ASCII characters corresponding to the unicode char. + */ +static inline int asc2unichar(struct super_block *sb, const char *astr, int len, + wchar_t *uc) { - struct nls_table *nls = HFSPLUS_SB(sb).nls; - int size, off, decompose; + int size = HFSPLUS_SB(sb).nls->char2uni(astr, len, uc); + if (size <= 0) { + *uc = '?'; + size = 1; + } + switch (*uc) { + case 0x2400: + *uc = 0; + break; + case ':': + *uc = '/'; + break; + } + return size; +} + +/* Decomposes a single unicode character. */ +static inline u16 *decompose_unichar(wchar_t uc, int *size) +{ + int off; + + off = hfsplus_decompose_table[(uc >> 12) & 0xf]; + if (off == 0 || off == 0xffff) + return NULL; + + off = hfsplus_decompose_table[off + ((uc >> 8) & 0xf)]; + if (!off) + return NULL; + + off = hfsplus_decompose_table[off + ((uc >> 4) & 0xf)]; + if (!off) + return NULL; + + off = hfsplus_decompose_table[off + (uc & 0xf)]; + *size = off & 3; + if (*size == 0) + return NULL; + return hfsplus_decompose_table + (off / 4); +} + +int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, + const char *astr, int len) +{ + int size, dsize, decompose; + u16 *dstr, outlen = 0; wchar_t c; - u16 outlen = 0; decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE); - while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { - size = nls->char2uni(astr, len, &c); - if (size <= 0) { - c = '?'; - size = 1; - } - astr += size; - len -= size; - switch (c) { - case 0x2400: - c = 0; - break; - case ':': - c = '/'; - break; - } - if (c >= 0xc0 && decompose) { - off = hfsplus_decompose_table[(c >> 12) & 0xf]; - if (!off) - goto done; - if (off == 0xffff) { - goto done; - } - off = hfsplus_decompose_table[off + ((c >> 8) & 0xf)]; - if (!off) - goto done; - off = hfsplus_decompose_table[off + ((c >> 4) & 0xf)]; - if (!off) - goto done; - off = hfsplus_decompose_table[off + (c & 0xf)]; - size = off & 3; - if (!size) - goto done; - off /= 4; - if (outlen + size > HFSPLUS_MAX_STRLEN) + size = asc2unichar(sb, astr, len, &c); + + if (decompose && (dstr = decompose_unichar(c, &dsize))) { + if (outlen + dsize > HFSPLUS_MAX_STRLEN) break; do { - ustr->unicode[outlen++] = cpu_to_be16(hfsplus_decompose_table[off++]); - } while (--size > 0); - continue; - } - done: - ustr->unicode[outlen++] = cpu_to_be16(c); + ustr->unicode[outlen++] = cpu_to_be16(*dstr++); + } while (--dsize > 0); + } else + ustr->unicode[outlen++] = cpu_to_be16(c); + + astr += size; + len -= size; } ustr->length = cpu_to_be16(outlen); if (len > 0)