]> git.sur5r.net Git - openldap/blob - libraries/libldap/utf-8-conv.c
unifdef -DLDAP_NOCACHE
[openldap] / libraries / libldap / utf-8-conv.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 2000-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 /* $Novell: /ldap/src/cldap/libraries/libldap/utfconv.c,v 1.3 2000/12/11 19:35:37 dsteck Exp $ */
8 /******************************************************************************
9  * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
10  * 
11  * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
12  * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
13  * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
14  * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
15  * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
16  * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
17  * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
18  * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. 
19  ******************************************************************************/
20 /* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License 
21  * can be found in the file "build/LICENSE-2.0.1" in this distribution
22  * of OpenLDAP Software.
23  */
24
25 /*
26  * UTF-8 Conversion Routines
27  *
28  * These routines convert between Wide Character and UTF-8,
29  * or between MultiByte and UTF-8 encodings.
30  *
31  * Both single character and string versions of the functions are provided.
32  * All functions return -1 if the character or string cannot be converted.
33  */
34
35 #include "portable.h"
36
37 #include <stdio.h>
38 #include <ac/stdlib.h>          /* For wctomb, wcstombs, mbtowc, mbstowcs */
39 #include <ac/string.h>
40 #include <ac/time.h>            /* for time_t */
41
42 #include "ldap-int.h"
43
44 #include <ldap_utf8.h>
45
46 static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
47
48
49 /*-----------------------------------------------------------------------------
50                                         UTF-8 Format Summary
51
52 ASCII chars                                             7 bits
53     0xxxxxxx
54     
55 2-character UTF-8 sequence:        11 bits
56     110xxxxx  10xxxxxx
57
58 3-character UTF-8                  16 bits
59     1110xxxx  10xxxxxx  10xxxxxx   
60     
61 4-char UTF-8                       21 bits 
62     11110xxx  10xxxxxx  10xxxxxx  10xxxxxx
63     
64 5-char UTF-8                       26 bits
65     111110xx  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx
66     
67 6-char UTF-8                       31 bits
68     1111110x  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx
69     
70 Unicode address space   (0 - 0x10FFFF)    21 bits
71 ISO-10646 address space (0 - 0x7FFFFFFF)  31 bits
72
73 Note:  This code does not prevent UTF-8 sequences which are longer than
74            necessary from being decoded.
75 */
76
77 /*----------------------------------------------------------------------------- 
78    Convert a UTF-8 character to a wide char. 
79    Return the length of the UTF-8 input character in bytes.
80 */
81 int
82 ldap_x_utf8_to_wc ( wchar_t *wchar, const char *utf8char )
83 {
84         int utflen, i;
85         wchar_t ch;
86
87         /* If input ptr is NULL, treat it as empty string. */
88         if (utf8char == NULL)
89                 utf8char = "";
90
91         /* Get UTF-8 sequence length from 1st byte */
92         utflen = LDAP_UTF8_CHARLEN2(utf8char, utflen);
93         
94         if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN )
95                 return -1;                                                                      /* Invalid input */
96
97         /* First byte minus length tag */
98         ch = (wchar_t)(utf8char[0] & mask[utflen]);
99         
100         for(i=1; i < utflen; i++)
101         {
102                 /* Subsequent bytes must start with 10 */
103                 if ((utf8char[i] & 0xc0) != 0x80)
104                         return -1;
105         
106                 ch <<= 6;                       /* 6 bits of data in each subsequent byte */
107                 ch |= (wchar_t)(utf8char[i] & 0x3f);
108         }
109         
110         if (wchar)
111                 *wchar = ch;
112
113         return utflen;
114 }
115
116 /*-----------------------------------------------------------------------------
117    Convert a UTF-8 string to a wide char string.
118    No more than 'count' wide chars will be written to the output buffer.
119    Return the size of the converted string in wide chars, excl null terminator.
120 */
121 int
122 ldap_x_utf8s_to_wcs ( wchar_t *wcstr, const char *utf8str, size_t count )
123 {
124         size_t wclen = 0;
125         int utflen, i;
126         wchar_t ch;
127
128
129         /* If input ptr is NULL, treat it as empty string. */
130         if (utf8str == NULL)
131                 utf8str = "";
132
133         /* Examine next UTF-8 character.  If output buffer is NULL, ignore count */
134         while ( *utf8str && (wcstr==NULL || wclen<count) )
135         {
136                 /* Get UTF-8 sequence length from 1st byte */
137                 utflen = LDAP_UTF8_CHARLEN2(utf8str, utflen);
138                 
139                 if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN )
140                         return -1;                                                                      /* Invalid input */
141
142                 /* First byte minus length tag */
143                 ch = (wchar_t)(utf8str[0] & mask[utflen]);
144                 
145                 for(i=1; i < utflen; i++)
146                 {
147                         /* Subsequent bytes must start with 10 */
148                         if ((utf8str[i] & 0xc0) != 0x80)
149                                 return -1;
150                 
151                         ch <<= 6;                       /* 6 bits of data in each subsequent byte */
152                         ch |= (wchar_t)(utf8str[i] & 0x3f);
153                 }
154                 
155                 if (wcstr)
156                         wcstr[wclen] = ch;
157                 
158                 utf8str += utflen;              /* Move to next UTF-8 character */
159                 wclen++;                                /* Count number of wide chars stored/required */
160         }
161
162         /* Add null terminator if there's room in the buffer. */
163         if (wcstr && wclen < count)
164                 wcstr[wclen] = 0;
165
166         return wclen;
167 }
168
169
170 /*----------------------------------------------------------------------------- 
171    Convert one wide char to a UTF-8 character.
172    Return the length of the converted UTF-8 character in bytes.
173    No more than 'count' bytes will be written to the output buffer.
174 */
175 int
176 ldap_x_wc_to_utf8 ( char *utf8char, wchar_t wchar, size_t count )
177 {
178         int len=0;
179
180         if (utf8char == NULL)   /* Just determine the required UTF-8 char length. */
181         {                                               /* Ignore count */
182                 if( wchar < 0 )
183                         return -1;
184                 if( wchar < 0x80 )
185                         return 1;
186                 if( wchar < 0x800 )
187                         return 2; 
188                 if( wchar < 0x10000 )
189                         return 3;
190                 if( wchar < 0x200000 ) 
191                         return 4;
192                 if( wchar < 0x4000000 ) 
193                         return 5;
194                 if( wchar < 0x80000000 )
195                         return 6;
196                 return -1;
197         }
198
199         
200         if ( wchar < 0 ) {                              /* Invalid wide character */
201                 len = -1;
202
203         } else if( wchar < 0x80 ) {
204                 if (count >= 1) {
205                         utf8char[len++] = (char)wchar;
206                 }
207
208         } else if( wchar < 0x800 ) {
209                 if (count >=2) {
210                         utf8char[len++] = 0xc0 | ( wchar >> 6 );
211                         utf8char[len++] = 0x80 | ( wchar & 0x3f );
212                 }
213
214         } else if( wchar < 0x10000 ) {
215                 if (count >= 3) {       
216                         utf8char[len++] = 0xe0 | ( wchar >> 12 );
217                         utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
218                         utf8char[len++] = 0x80 | ( wchar & 0x3f );
219                 }
220         
221         } else if( wchar < 0x200000 ) {
222                 if (count >= 4) {
223                         utf8char[len++] = 0xf0 | ( wchar >> 18 );
224                         utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
225                         utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
226                         utf8char[len++] = 0x80 | ( wchar & 0x3f );
227                 }
228
229         } else if( wchar < 0x4000000 ) {
230                 if (count >= 5) {
231                         utf8char[len++] = 0xf8 | ( wchar >> 24 );
232                         utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f );
233                         utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
234                         utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
235                         utf8char[len++] = 0x80 | ( wchar & 0x3f );
236                 }
237
238         } else if( wchar < 0x80000000 ) {
239                 if (count >= 6) {
240                         utf8char[len++] = 0xfc | ( wchar >> 30 );
241                         utf8char[len++] = 0x80 | ( (wchar >> 24) & 0x3f );
242                         utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f );
243                         utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
244                         utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
245                         utf8char[len++] = 0x80 | ( wchar & 0x3f );
246                 }
247
248         } else
249                 len = -1;
250         
251         return len;
252
253 }
254
255
256 /*-----------------------------------------------------------------------------
257    Convert a wide char string to a UTF-8 string.
258    No more than 'count' bytes will be written to the output buffer.
259    Return the # of bytes written to the output buffer, excl null terminator.
260 */
261 int
262 ldap_x_wcs_to_utf8s ( char *utf8str, const wchar_t *wcstr, size_t count )
263 {
264         int len = 0;
265         int n;
266         char *p = utf8str;
267         wchar_t empty = 0;              /* To avoid use of L"" construct */
268
269         if (wcstr == NULL)              /* Treat input ptr NULL as an empty string */
270                 wcstr = &empty;
271
272         if (utf8str == NULL)    /* Just compute size of output, excl null */
273         {
274                 while (*wcstr)
275                 {
276                         /* Get UTF-8 size of next wide char */
277                         n = ldap_x_wc_to_utf8( NULL, *wcstr++, LDAP_MAX_UTF8_LEN);
278                         if (n == -1)
279                                 return -1;
280                         len += n;
281                 }
282
283                 return len;
284         }
285
286         
287         /* Do the actual conversion. */
288
289         n = 1;                                  /* In case of empty wcstr */
290         while (*wcstr)
291         {
292                 n = ldap_x_wc_to_utf8( p, *wcstr++, count);
293                 
294                 if (n <= 0)             /* If encoding error (-1) or won't fit (0), quit */
295                         break;
296                 
297                 p += n;
298                 count -= n;                     /* Space left in output buffer */
299         }
300
301         /* If not enough room for last character, pad remainder with null
302            so that return value = original count, indicating buffer full. */
303         if (n == 0)
304         {
305                 while (count--)
306                         *p++ = 0;
307         }
308
309         /* Add a null terminator if there's room. */
310         else if (count)
311                 *p = 0;
312
313         if (n == -1)                    /* Conversion encountered invalid wide char. */
314                 return -1;
315
316         /* Return the number of bytes written to output buffer, excl null. */ 
317         return (p - utf8str);
318 }
319
320
321 /*-----------------------------------------------------------------------------
322    Convert a UTF-8 character to a MultiByte character.
323    Return the size of the converted character in bytes.
324 */
325 int
326 ldap_x_utf8_to_mb ( char *mbchar, const char *utf8char,
327                 int (*f_wctomb)(char *mbchar, wchar_t wchar) )
328 {
329         wchar_t wchar;
330         int n;
331         char tmp[6];                            /* Large enough for biggest multibyte char */
332
333         if (f_wctomb == NULL)           /* If no conversion function was given... */
334                 f_wctomb = wctomb;              /*    use the local ANSI C function */
335  
336         /* First convert UTF-8 char to a wide char */
337         n = ldap_x_utf8_to_wc( &wchar, utf8char);
338
339         if (n == -1)
340                 return -1;              /* Invalid UTF-8 character */
341
342         if (mbchar == NULL)
343                 n = f_wctomb( tmp, wchar );
344         else
345                 n = f_wctomb( mbchar, wchar);
346
347         return n;
348 }
349
350 /*-----------------------------------------------------------------------------
351    Convert a UTF-8 string to a MultiByte string.
352    No more than 'count' bytes will be written to the output buffer.
353    Return the size of the converted string in bytes, excl null terminator.
354 */
355 int
356 ldap_x_utf8s_to_mbs ( char *mbstr, const char *utf8str, size_t count,
357                 size_t (*f_wcstombs)(char *mbstr, const wchar_t *wcstr, size_t count) )
358 {
359         wchar_t *wcs;
360         size_t wcsize;
361     int n;
362
363         if (f_wcstombs == NULL)         /* If no conversion function was given... */
364                 f_wcstombs = wcstombs;  /*    use the local ANSI C function */
365  
366         if (utf8str == NULL || *utf8str == 0)   /* NULL or empty input string */
367         {
368                 if (mbstr)
369                         *mbstr = 0;
370                 return 0;
371         }
372
373 /* Allocate memory for the maximum size wchar string that we could get. */
374         wcsize = strlen(utf8str) + 1;
375         wcs = (wchar_t *)LDAP_MALLOC(wcsize * sizeof(wchar_t));
376         if (wcs == NULL)
377                 return -1;                              /* Memory allocation failure. */
378
379         /* First convert the UTF-8 string to a wide char string */
380         n = ldap_x_utf8s_to_wcs( wcs, utf8str, wcsize);
381
382         /* Then convert wide char string to multi-byte string */
383         if (n != -1)
384         {
385                 n = f_wcstombs(mbstr, wcs, count);
386         }
387
388         LDAP_FREE(wcs);
389
390         return n;
391 }
392
393 /*-----------------------------------------------------------------------------
394    Convert a MultiByte character to a UTF-8 character.
395    'mbsize' indicates the number of bytes of 'mbchar' to check.
396    Returns the number of bytes written to the output character.
397 */
398 int
399 ldap_x_mb_to_utf8 ( char *utf8char, const char *mbchar, size_t mbsize,
400                 int (*f_mbtowc)(wchar_t *wchar, const char *mbchar, size_t count) )
401 {
402     wchar_t wchar;
403     int n;
404
405         if (f_mbtowc == NULL)           /* If no conversion function was given... */
406                 f_mbtowc = mbtowc;              /*    use the local ANSI C function */
407  
408     if (mbsize == 0)                            /* 0 is not valid. */
409         return -1;
410
411     if (mbchar == NULL || *mbchar == 0)
412     {
413         if (utf8char)
414             *utf8char = 0;
415         return 1;
416     }
417
418         /* First convert the MB char to a Wide Char */
419         n = f_mbtowc( &wchar, mbchar, mbsize);
420
421         if (n == -1)
422                 return -1;
423
424         /* Convert the Wide Char to a UTF-8 character. */
425         n = ldap_x_wc_to_utf8( utf8char, wchar, LDAP_MAX_UTF8_LEN);
426
427         return n;
428 }
429
430
431 /*-----------------------------------------------------------------------------
432    Convert a MultiByte string to a UTF-8 string.
433    No more than 'count' bytes will be written to the output buffer.
434    Return the size of the converted string in bytes, excl null terminator.
435 */   
436 int
437 ldap_x_mbs_to_utf8s ( char *utf8str, const char *mbstr, size_t count,
438                 size_t (*f_mbstowcs)(wchar_t *wcstr, const char *mbstr, size_t count) )
439 {
440         wchar_t *wcs;
441         int n;
442         size_t wcsize;
443
444         if (mbstr == NULL)                 /* Treat NULL input string as an empty string */
445                 mbstr = "";
446
447         if (f_mbstowcs == NULL)         /* If no conversion function was given... */
448                 f_mbstowcs = mbstowcs;  /*    use the local ANSI C function */
449  
450         /* Allocate memory for the maximum size wchar string that we could get. */
451         wcsize = strlen(mbstr) + 1;
452         wcs = (wchar_t *)LDAP_MALLOC( wcsize * sizeof(wchar_t) );
453         if (wcs == NULL)
454                 return -1;
455
456         /* First convert multi-byte string to a wide char string */
457         n = f_mbstowcs(wcs, mbstr, wcsize);
458
459         /* Convert wide char string to UTF-8 string */
460         if (n != -1)
461         {
462                 n = ldap_x_wcs_to_utf8s( utf8str, wcs, count);
463         }
464
465         LDAP_FREE(wcs);
466
467         return n;       
468 }