f979f429ff7d4306c3b4a826c2c45712d2f37af4
[mono.git] / eglib / src / gunicode.c
1 /*
2  * gunicode.c: Some Unicode routines 
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@novell.com)
6  *
7  * (C) 2006 Novell, Inc.
8  *
9  * utf8 validation code came from:
10  *      libxml2-2.6.26 licensed under the MIT X11 license
11  *
12  * Authors credit in libxml's string.c:
13  *   William Brack <wbrack@mmm.com.hk>
14  *   daniel@veillard.com
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining
17  * a copy of this software and associated documentation files (the
18  * "Software"), to deal in the Software without restriction, including
19  * without limitation the rights to use, copy, modify, merge, publish,
20  * distribute, sublicense, and/or sell copies of the Software, and to
21  * permit persons to whom the Software is furnished to do so, subject to
22  * the following conditions:
23  *
24  * The above copyright notice and this permission notice shall be
25  * included in all copies or substantial portions of the Software.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34  *
35  */
36 #include <config.h>
37 #include <stdio.h>
38 #include <glib.h>
39 #include <unicode-data.h>
40 #include <errno.h>
41
42 #if defined(_MSC_VER) || defined(G_OS_WIN32)
43 /* FIXME */
44 #  define CODESET 1
45 #  include <windows.h>
46 #else
47 #    ifdef HAVE_LOCALCHARSET_H
48 #       include <localcharset.h>
49 #    endif
50 #endif
51
52 static const char *my_charset;
53 static gboolean is_utf8;
54
55 /*
56  * Character set conversion
57  */
58
59 GUnicodeType 
60 g_unichar_type (gunichar c)
61 {
62         int i;
63
64         guint16 cp = (guint16) c;
65         for (i = 0; i < unicode_category_ranges_count; i++) {
66                 if (cp < unicode_category_ranges [i].start)
67                         continue;
68                 if (unicode_category_ranges [i].end <= cp)
69                         continue;
70                 return unicode_category [i] [cp - unicode_category_ranges [i].start];
71         }
72
73         /*
74         // 3400-4DB5: OtherLetter
75         // 4E00-9FC3: OtherLetter
76         // AC00-D7A3: OtherLetter
77         // D800-DFFF: OtherSurrogate
78         // E000-F8FF: OtherPrivateUse
79         // 20000-2A6D6 OtherLetter
80         // F0000-FFFFD OtherPrivateUse
81         // 100000-10FFFD OtherPrivateUse
82         */
83         if (0x3400 <= cp && cp < 0x4DB5)
84                 return G_UNICODE_OTHER_LETTER;
85         if (0x4E00 <= cp && cp < 0x9FC3)
86                 return G_UNICODE_OTHER_LETTER;
87         if (0xAC00<= cp && cp < 0xD7A3)
88                 return G_UNICODE_OTHER_LETTER;
89         if (0xD800 <= cp && cp < 0xDFFF)
90                 return G_UNICODE_SURROGATE;
91         if (0xE000 <= cp && cp < 0xF8FF)
92                 return G_UNICODE_PRIVATE_USE;
93         /* since the argument is UTF-16, we cannot check beyond FFFF */
94
95         /* It should match any of above */
96         return 0;
97 }
98
99 GUnicodeBreakType
100 g_unichar_break_type (gunichar c)
101 {
102         // MOONLIGHT_FIXME
103         return G_UNICODE_BREAK_UNKNOWN;
104 }
105
106 gunichar
107 g_unichar_case (gunichar c, gboolean upper)
108 {
109         gint8 i, i2;
110         guint32 cp = (guint32) c, v;
111
112         for (i = 0; i < simple_case_map_ranges_count; i++) {
113                 if (cp < simple_case_map_ranges [i].start)
114                         return c;
115                 if (simple_case_map_ranges [i].end <= cp)
116                         continue;
117                 if (c < 0x10000) {
118                         const guint16 *tab = upper ? simple_upper_case_mapping_lowarea [i] : simple_lower_case_mapping_lowarea [i];
119                         v = tab [cp - simple_case_map_ranges [i].start];
120                 } else {
121                         const guint32 *tab;
122                         i2 = (gint8)(i - (upper ? simple_upper_case_mapping_lowarea_table_count : simple_lower_case_mapping_lowarea_table_count));
123                         tab = upper ? simple_upper_case_mapping_higharea [i2] : simple_lower_case_mapping_higharea [i2];
124                         v = tab [cp - simple_case_map_ranges [i].start];
125                 }
126                 return v != 0 ? (gunichar) v : c;
127         }
128         return c;
129 }
130
131 gunichar
132 g_unichar_toupper (gunichar c)
133 {
134         return g_unichar_case (c, TRUE);
135 }
136
137 gunichar
138 g_unichar_tolower (gunichar c)
139 {
140         return g_unichar_case (c, FALSE);
141 }
142
143 gunichar
144 g_unichar_totitle (gunichar c)
145 {
146         guint8 i;
147         guint32 cp;
148
149         cp = (guint32) c;
150         for (i = 0; i < simple_titlecase_mapping_count; i++) {
151                 if (simple_titlecase_mapping [i].codepoint == cp)
152                         return simple_titlecase_mapping [i].title;
153                 if (simple_titlecase_mapping [i].codepoint > cp)
154                         /* it is ordered, hence no more match */
155                         break;
156         }
157         return g_unichar_toupper (c);
158 }
159
160 gboolean
161 g_unichar_isxdigit (gunichar c)
162 {
163         return (g_unichar_xdigit_value (c) != -1);
164
165 }
166
167 gint
168 g_unichar_xdigit_value (gunichar c)
169 {
170         if (c >= 0x30 && c <= 0x39) /*0-9*/
171                 return (c - 0x30);
172         if (c >= 0x41 && c <= 0x46) /*A-F*/
173                 return (c - 0x37);
174         if (c >= 0x61 && c <= 0x66) /*a-f*/
175                 return (c - 0x57);
176         return -1;
177 }
178
179 gboolean
180 g_unichar_isspace (gunichar c)
181 {
182         GUnicodeType type = g_unichar_type (c);
183         if (type == G_UNICODE_LINE_SEPARATOR ||
184             type == G_UNICODE_PARAGRAPH_SEPARATOR ||
185             type == G_UNICODE_SPACE_SEPARATOR)
186                 return TRUE;
187
188         return FALSE;
189 }
190
191
192 /*
193  * This is broken, and assumes an UTF8 system, but will do for eglib's first user
194  */
195 gchar *
196 g_filename_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
197 {
198         char *res;
199         
200         if (len == -1)
201                 len = strlen (utf8string);
202
203         res = g_malloc (len + 1);
204         g_strlcpy (res, utf8string, len + 1);
205         return res;
206 }
207
208 #ifdef G_OS_WIN32
209 extern WINBASEAPI UINT WINAPI GetACP(void);
210 gboolean
211 g_get_charset (G_CONST_RETURN char **charset)
212 {
213         if (my_charset == NULL) {
214                 static char buf [14];
215 #if G_HAVE_API_SUPPORT(HAVE_UWP_WINAPI_SUPPORT)
216                 CPINFOEXA cp_info;
217                 GetCPInfoExA (CP_ACP, 0, &cp_info);
218                 sprintf (buf, "CP%u", cp_info.CodePage);
219 #else
220                 sprintf (buf, "CP%u", GetACP ());
221 #endif
222                 my_charset = buf;
223                 is_utf8 = FALSE;
224         }
225         
226         if (charset != NULL)
227                 *charset = my_charset;
228
229         return is_utf8;
230 }
231
232 #else /* G_OS_WIN32 */
233
234 gboolean
235 g_get_charset (G_CONST_RETURN char **charset)
236 {
237         if (my_charset == NULL) {
238                 /* These shouldn't be heap allocated */
239 #if defined(HAVE_LOCALCHARSET_H)
240                 my_charset = locale_charset ();
241 #else
242                 my_charset = "UTF-8";
243 #endif
244                 is_utf8 = strcmp (my_charset, "UTF-8") == 0;
245         }
246         
247         if (charset != NULL)
248                 *charset = my_charset;
249
250         return is_utf8;
251 }
252 #endif /* G_OS_WIN32 */
253
254 gchar *
255 g_locale_to_utf8 (const gchar *opsysstring, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
256 {
257         g_get_charset (NULL);
258
259         return g_convert (opsysstring, len, "UTF-8", my_charset, bytes_read, bytes_written, error);
260 }
261
262 gchar *
263 g_locale_from_utf8 (const gchar *utf8string, gssize len, gsize *bytes_read, gsize *bytes_written, GError **error)
264 {
265         g_get_charset (NULL);
266
267         return g_convert (utf8string, len, my_charset, "UTF-8", bytes_read, bytes_written, error);
268 }