ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / hfsplus / unicode.c
1 /*
2  *  linux/fs/hfsplus/unicode.c
3  *
4  * Copyright (C) 2001
5  * Brad Boyer (flar@allandria.com)
6  * (C) 2003 Ardis Technologies <roman@ardistech.com>
7  *
8  * Handler routines for unicode strings
9  */
10
11 #include <linux/types.h>
12 #include <linux/nls.h>
13 #include "hfsplus_fs.h"
14 #include "hfsplus_raw.h"
15
16 /* Fold the case of a unicode char, given the 16 bit value */
17 /* Returns folded char, or 0 if ignorable */
18 static inline u16 case_fold(u16 c)
19 {
20         u16 tmp;
21
22         tmp = case_fold_table[(c>>8)];
23         if (tmp)
24                 tmp = case_fold_table[tmp + (c & 0xFF)];
25         else
26                 tmp = c;
27         return tmp;
28 }
29
30 /* Compare unicode strings, return values like normal strcmp */
31 int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unistr *s2)
32 {
33         u16 len1, len2, c1, c2;
34         const hfsplus_unichr *p1, *p2;
35
36         len1 = be16_to_cpu(s1->length);
37         len2 = be16_to_cpu(s2->length);
38         p1 = s1->unicode;
39         p2 = s2->unicode;
40
41         while (1) {
42                 c1 = c2 = 0;
43
44                 while (len1 && !c1) {
45                         c1 = case_fold(be16_to_cpu(*p1));
46                         p1++;
47                         len1--;
48                 }
49                 while (len2 && !c2) {
50                         c2 = case_fold(be16_to_cpu(*p2));
51                         p2++;
52                         len2--;
53                 }
54
55                 if (c1 != c2)
56                         return (c1 < c2) ? -1 : 1;
57                 if (!c1 && !c2)
58                         return 0;
59         }
60 }
61
62 int hfsplus_uni2asc(const struct hfsplus_unistr *ustr, char *astr, int *len)
63 {
64         const hfsplus_unichr *ip;
65         u8 *op;
66         u16 ustrlen, cc;
67         int size, tmp;
68
69         op = astr;
70         ip = ustr->unicode;
71         ustrlen = be16_to_cpu(ustr->length);
72         tmp = *len;
73         while (ustrlen > 0 && tmp > 0) {
74                 cc = be16_to_cpu(*ip);
75                 switch (cc) {
76                 case 0:
77                         cc = 0x2400;
78                         break;
79                 case '/':
80                         cc = ':';
81                         break;
82                 }
83                 if (cc > 0x7f) {
84                         size = utf8_wctomb(op, cc, tmp);
85                         if (size == -1) {
86                                 /* ignore */
87                         } else {
88                                 op += size;
89                                 tmp -= size;
90                         }
91                 } else {
92                         *op++ = (u8) cc;
93                         tmp--;
94                 }
95                 ip++;
96                 ustrlen--;
97         }
98         *len = (char *)op - astr;
99         if (ustrlen)
100                 return -ENAMETOOLONG;
101         return 0;
102 }
103
104 int hfsplus_asc2uni(struct hfsplus_unistr *ustr, const char *astr, int len)
105 {
106         int tmp;
107         wchar_t c;
108         u16 outlen = 0;
109
110         while (outlen <= HFSPLUS_MAX_STRLEN && len > 0) {
111                 if (*astr & 0x80) {
112                         tmp = utf8_mbtowc(&c, astr, len);
113                         if (tmp < 0) {
114                                 astr++;
115                                 len--;
116                                 continue;
117                         } else {
118                                 astr += tmp;
119                                 len -= tmp;
120                         }
121                 } else {
122                         c = *astr++;
123                         len--;
124                 }
125                 switch (c) {
126                 case 0x2400:
127                         c = 0;
128                         break;
129                 case ':':
130                         c = '/';
131                         break;
132                 }
133                 ustr->unicode[outlen] = cpu_to_be16(c);
134                 outlen++;
135         }
136         ustr->length = cpu_to_be16(outlen);
137         if (len > 0)
138                 return -ENAMETOOLONG;
139         return 0;
140 }