Initial pass at adding unicode support functions. A few things still need
[platform/upstream/glib.git] / gutf8.c
1 /* gutf8.c - Operations on UTF-8 strings.
2  *
3  * Copyright (C) 1999 Tom Tromey
4  * Copyright (C) 2000 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <config.h>
23
24 #include <stdlib.h>
25 #ifdef HAVE_LANGINFO_H
26 #include <langinfo.h>
27 #endif
28 #include <string.h>
29
30 #include "glib.h"
31
32 #define UTF8_COMPUTE(Char, Mask, Len)                                         \
33   if (Char < 128)                                                             \
34     {                                                                         \
35       Len = 1;                                                                \
36       Mask = 0x7f;                                                            \
37     }                                                                         \
38   else if ((Char & 0xe0) == 0xc0)                                             \
39     {                                                                         \
40       Len = 2;                                                                \
41       Mask = 0x1f;                                                            \
42     }                                                                         \
43   else if ((Char & 0xf0) == 0xe0)                                             \
44     {                                                                         \
45       Len = 3;                                                                \
46       Mask = 0x0f;                                                            \
47     }                                                                         \
48   else if ((Char & 0xf8) == 0xf0)                                             \
49     {                                                                         \
50       Len = 4;                                                                \
51       Mask = 0x07;                                                            \
52     }                                                                         \
53   else if ((Char & 0xfc) == 0xf8)                                             \
54     {                                                                         \
55       Len = 5;                                                                \
56       Mask = 0x03;                                                            \
57     }                                                                         \
58   else if ((Char & 0xfe) == 0xfc)                                             \
59     {                                                                         \
60       Len = 6;                                                                \
61       Mask = 0x01;                                                            \
62     }                                                                         \
63   else                                                                        \
64     Len = -1;
65
66 #define UTF8_GET(Result, Chars, Count, Mask, Len)                             \
67   (Result) = (Chars)[0] & (Mask);                                             \
68   for ((Count) = 1; (Count) < (Len); ++(Count))                               \
69     {                                                                         \
70       if (((Chars)[(Count)] & 0xc0) != 0x80)                                  \
71         {                                                                     \
72           (Result) = -1;                                                      \
73           break;                                                              \
74         }                                                                     \
75       (Result) <<= 6;                                                         \
76       (Result) |= ((Chars)[(Count)] & 0x3f);                                  \
77     }
78
79 gchar g_utf8_skip[256] = {
80   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
81   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
82   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
83   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
84   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
85   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
86   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
87   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0
88 };
89
90 /**
91  * g_utf8_find_prev_char:
92  * @str: pointer to the beginning of a UTF-8 string
93  * @p: pointer to some position within @str
94  * 
95  * Given a position @p with a UTF-8 encoded string @str, find the start
96  * of the previous UTF-8 character starting before @p. Returns %NULL if no
97  * UTF-8 characters are present in @p before @str.
98  *
99  * @p does not have to be at the beginning of a UTF-8 chracter. No check
100  * is made to see if the character found is actually valid other than
101  * it starts with an appropriate byte.
102  *
103  * Return value: a pointer to the found character or %NULL.
104  **/
105 gchar *
106 g_utf8_find_prev_char (const char *str,
107                        const char *p)
108 {
109   for (--p; p > str; --p)
110     {
111       if ((*p & 0xc0) != 0x80)
112         return (gchar *)p;
113     }
114   return NULL;
115 }
116
117 /**
118  * g_utf8_find_next_char:
119  * @p: a pointer to a position within a UTF-8 encoded string
120  * @end: a pointer to the end of the string, or %NULL to indicate
121  *        that the string is NULL terminated, in which case
122  *        the returned value will be 
123  *
124  * Find the start of the next utf-8 character in the string after @p
125  *
126  * @p does not have to be at the beginning of a UTF-8 chracter. No check
127  * is made to see if the character found is actually valid other than
128  * it starts with an appropriate byte.
129  * 
130  * Return value: a pointer to the found character or %NULL
131  **/
132 gchar *
133 g_utf8_find_next_char (const gchar *p,
134                        const gchar *end)
135 {
136   if (*p)
137     {
138       if (end)
139         for (++p; p < end && (*p & 0xc0) == 0x80; ++p)
140           ;
141       else
142         for (++p; (*p & 0xc0) == 0x80; ++p)
143           ;
144     }
145   return (p == end) ? NULL : (gchar *)p;
146 }
147
148 /**
149  * g_utf8_prev_char:
150  * @p: a pointer to a position within a UTF-8 encoded string
151  *
152  * Find the previous UTF-8 character in the string before @p
153  *
154  * @p does not have to be at the beginning of a UTF-8 character. No check
155  * is made to see if the character found is actually valid other than
156  * it starts with an appropriate byte. If @p might be the first
157  * character of the string, you must use g_utf8_find_prev_char instead.
158  * 
159  * Return value: a pointer to the found character.
160  **/
161 gchar *
162 g_utf8_prev_char (const gchar *p)
163 {
164   while (TRUE)
165     {
166       p--;
167       if ((*p & 0xc0) != 0x80)
168         return (gchar *)p;
169     }
170 }
171
172 /**
173  * g_utf8_strlen:
174  * @p: pointer to the start of a UTF-8 string.
175  * @max: the maximum number of bytes to examine. If @max
176  *       is less than 0, then the string is assumed to be
177  *       nul-terminated.
178  * 
179  * Return value: the length of the string in characters
180  */
181 gint
182 g_utf8_strlen (const gchar *p, gint max)
183 {
184   int len = 0;
185   const gchar *start = p;
186   /* special case for the empty string */
187   if (!*p) 
188     return 0;
189   /* Note that the test here and the test in the loop differ subtly.
190      In the loop we want to see if we've passed the maximum limit --
191      for instance if the buffer ends mid-character.  Here at the top
192      of the loop we want to see if we've just reached the last byte.  */
193   while (max < 0 || p - start < max)
194     {
195       p = g_utf8_next_char (p);
196       ++len;
197       if (! *p || (max > 0 && p - start > max))
198         break;
199     }
200   return len;
201 }
202
203 /**
204  * g_utf8_get_char:
205  * @p: a pointer to unicode character encoded as UTF-8
206  * 
207  * Convert a sequence of bytes encoded as UTF-8 to a unicode character.
208  * 
209  * Return value: the resulting character or (gunichar)-1 if @p does
210  *               not point to a valid UTF-8 encoded unicode character
211  **/
212 gunichar
213 g_utf8_get_char (const gchar *p)
214 {
215   int i, mask = 0, len;
216   gunichar result;
217   unsigned char c = (unsigned char) *p;
218
219   UTF8_COMPUTE (c, mask, len);
220   if (len == -1)
221     return (gunichar)-1;
222   UTF8_GET (result, p, i, mask, len);
223
224   return result;
225 }
226
227 /**
228  * g_utf8_offset_to_pointer:
229  * @str: a UTF-8 encoded string
230  * @offset: a character offset within the string.
231  * 
232  * Converts from an integer character offset to a pointer to a position
233  * within the string.
234  * 
235  * Return value: the resulting pointer
236  **/
237 gchar *
238 g_utf8_offset_to_pointer  (const gchar *str,
239                            gint         offset)
240 {
241   const gchar *s = str;
242   while (offset--)
243     s = g_utf8_next_char (s);
244   
245   return (gchar *)s;
246 }
247
248 /**
249  * g_utf8_pointer_to_offset:
250  * @str: a UTF-8 encoded string
251  * @pos: a pointer to a position within @str
252  * 
253  * Converts from a pointer to position within a string to a integer
254  * character offset
255  * 
256  * Return value: the resulting character offset
257  **/
258 gint
259 g_utf8_pointer_to_offset (const gchar *str,
260                           const gchar *pos)
261 {
262   const gchar *s = str;
263   gint offset = 0;
264   
265   while (s < pos)
266     {
267       s = g_utf8_next_char (s);
268       offset++;
269     }
270
271   return offset;
272 }
273
274
275 gchar *
276 g_utf8_strncpy (gchar *dest, const gchar *src, size_t n)
277 {
278   const gchar *s = src;
279   while (n && *s)
280     {
281       s = g_utf8_next_char(s);
282       n--;
283     }
284   strncpy(dest, src, s - src);
285   dest[s - src] = 0;
286   return dest;
287 }
288
289 static gboolean
290 g_utf8_get_charset_internal (char **a)
291 {
292   char *charset = getenv("CHARSET");
293
294   if (charset && a && ! *a)
295     *a = charset;
296
297   if (charset && strstr (charset, "UTF-8"))
298       return TRUE;
299
300 #ifdef _NL_CTYPE_CODESET_NAME
301   charset = nl_langinfo (_NL_CTYPE_CODESET_NAME);
302   if (charset)
303     {
304       if (a && ! *a)
305         *a = charset;
306       if (strcmp (charset, "UTF-8") == 0)
307         return TRUE;
308     }
309 #elif CODESET
310   charset = nl_langinfo(CODESET);
311   if (charset)
312     {
313       if (a && ! *a)
314         *a = charset;
315       if (strcmp (charset, "UTF-8") == 0)
316         return TRUE;
317     }
318 #endif  
319
320   if (a && ! *a) 
321     *a = "US-ASCII";
322   /* Assume this for compatibility at present.  */
323   return FALSE;
324 }
325
326 static int utf8_locale_cache = -1;
327 static char *utf8_charset_cache = NULL;
328
329 gboolean
330 g_get_charset (char **charset) 
331 {
332   if (utf8_locale_cache != -1)
333     {
334       if (charset)
335         *charset = utf8_charset_cache;
336       return utf8_locale_cache;
337     }
338   utf8_locale_cache = g_utf8_get_charset_internal (&utf8_charset_cache);
339   if (charset) 
340     *charset = utf8_charset_cache;
341   return utf8_locale_cache;
342 }
343
344 /* unicode_strchr */
345
346 /**
347  * g_unichar_to_utf8:
348  * @ch: a ISO10646 character code
349  * @out: output buffer, must have at least 6 bytes of space.
350  * 
351  * Convert a single character to utf8
352  * 
353  * Return value: number of bytes written
354  **/
355 int
356 g_unichar_to_utf8 (gunichar c, gchar *outbuf)
357 {
358   size_t len = 0;
359   int first;
360   int i;
361
362   if (c < 0x80)
363     {
364       first = 0;
365       len = 1;
366     }
367   else if (c < 0x800)
368     {
369       first = 0xc0;
370       len = 2;
371     }
372   else if (c < 0x10000)
373     {
374       first = 0xe0;
375       len = 3;
376     }
377    else if (c < 0x200000)
378     {
379       first = 0xf0;
380       len = 4;
381     }
382   else if (c < 0x4000000)
383     {
384       first = 0xf8;
385       len = 5;
386     }
387   else
388     {
389       first = 0xfc;
390       len = 6;
391     }
392
393   for (i = len - 1; i > 0; --i)
394     {
395       outbuf[i] = (c & 0x3f) | 0x80;
396       c >>= 6;
397     }
398   outbuf[0] = c | first;
399
400   return len;
401 }
402
403 /**
404  * g_utf8_strchr:
405  * @p: a nul-terminated utf-8 string
406  * @c: a iso-10646 character/
407  * 
408  * Find the leftmost occurence of the given iso-10646 character
409  * in a UTF-8 string.
410  * 
411  * Return value: NULL if the string does not contain the character, otherwise, a
412  *               a pointer to the start of the leftmost of the character in the string.
413  **/
414 gchar *
415 g_utf8_strchr (const char *p, gunichar c)
416 {
417   gchar ch[10];
418
419   gint len = g_unichar_to_utf8 (c, ch);
420   ch[len] = '\0';
421   
422   return strstr(p, ch);
423 }
424
425 #if 0
426 /**
427  * g_utf8_strrchr:
428  * @p: a nul-terminated utf-8 string
429  * @c: a iso-10646 character/
430  * 
431  * Find the rightmost occurence of the given iso-10646 character
432  * in a UTF-8 string.
433  * 
434  * Return value: NULL if the string does not contain the character, otherwise, a
435  *               a pointer to the start of the rightmost of the character in the string.
436  **/
437
438 /* This is ifdefed out atm as there is no strrstr function in libc.
439  */
440 gchar *
441 unicode_strrchr (const char *p, gunichar c)
442 {
443   gchar ch[10];
444
445   len = g_unichar_to_utf8 (c, ch);
446   ch[len] = '\0';
447   
448   return strrstr(p, ch);
449 }
450 #endif
451
452
453 /**
454  * g_utf8_to_ucs4:
455  * @str: a UTF-8 encoded strnig
456  * @len: the length of @
457  * 
458  * Convert a string from UTF-8 to a 32-bit fixed width
459  * representation as UCS-4.
460  * 
461  * Return value: a pointer to a newly allocated UCS-4 string.
462  *               This value must be freed with g_free()
463  **/
464 gunichar *
465 g_utf8_to_ucs4 (const char *str, int len)
466 {
467   gunichar *result;
468   gint n_chars, i;
469   const gchar *p;
470   
471   n_chars = g_utf8_strlen (str, len);
472   result = g_new (gunichar, n_chars);
473   
474   p = str;
475   for (i=0; i < n_chars; i++)
476     {
477       result[i] = g_utf8_get_char (p);
478       p = g_utf8_next_char (p);
479     }
480
481   return result;
482 }
483