]> git.sur5r.net Git - openldap/blob - libraries/libldap/utf-8.c
e14ebf84d842e79895719f88b3bd7aa1e4078a68
[openldap] / libraries / libldap / utf-8.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 /*
8  * Basic UTF-8 routines
9  *
10  * These routines are "dumb".  Though they understand UTF-8,
11  * they don't grok Unicode.  That is, they can push bits,
12  * but don't have a clue what the bits represent.  That's
13  * good enough for use with the LDAP Client SDK.
14  *
15  * These routines are not optimized.
16  */
17
18 #include "portable.h"
19
20 #include <stdio.h>
21
22 #include <ac/stdlib.h>
23
24 #include <ac/socket.h>
25 #include <ac/string.h>
26 #include <ac/time.h>
27
28 #include "ldap-int.h"
29 #include "ldap_defaults.h"
30
31 #define UTF8_ISASCII(u) ( (u) < 0x100 )
32 #define UCS4_INVALID    0x80000000U
33
34 /*
35  * Basic UTF-8 routines
36  */
37
38 /*
39  * return the number of bytes required to hold the
40  * NULL-terminated UTF-8 string INCLUDING the
41  * termination.
42  */
43 ber_len_t ldap_utf8_bytes( const char * p )
44 {
45         ber_len_t bytes = 0;
46
47         if( p == NULL ) return bytes;
48
49         while( p[bytes++] ) {
50                 /* EMPTY */ ;
51         }
52
53         return bytes;
54 }
55
56 ber_len_t ldap_utf8_chars( const char * p )
57 {
58         /* could be optimized and could check for invalid sequences */
59         ber_len_t chars;
60
61         for( chars=0; *p ; chars++ ) {
62                 int charlen = ldap_utf8_charlen( p );
63
64                 if( !charlen ) return chars;
65
66                 p = &p[charlen];
67         };
68
69         return chars;
70 }
71
72 int ldap_utf8_charlen( const char * p )
73 {
74         unsigned c = * (const unsigned char *) p;
75
76         if ((c & 0xfe ) == 0xfc) {
77                 return 6;
78         }
79
80         if ((c & 0xfc ) == 0xf8) {
81                 return 5;
82         }
83
84         if ((c & 0xf8 ) == 0xf0) {
85                 return 4;
86         }
87
88         if ((c & 0xf0 ) == 0xe0) {
89                 return 3;
90         }
91
92         if ((c & 0xe0 ) == 0xc0) {
93                 return 2;
94         }
95
96         if ((c & 0x80 ) == 0x80) {
97                 /* INVALID */
98                 return 0;
99         }
100
101         return 1;
102 }
103
104 /* conv UTF-8 to UCS-4, useful for comparisons */
105 ber_int_t ldap_utf8_to_ucs4( const char * p )
106 {
107         int len, i;
108     ber_int_t c = * (const unsigned char *) p;
109
110     if ((c & 0xFE ) == 0xFC) {
111         len = 6;
112                 c &= 0x01;
113
114     } else if ((c & 0xFC ) == 0xF8) {
115         len = 5;
116                 c &= 0x03;
117
118     } else if ((c & 0xF8 ) == 0xF0) {
119         len = 4;
120                 c &= 0x07;
121
122     } else if ((c & 0xF0 ) == 0xE0) {
123         len = 3;
124                 c &= 0x0F;
125
126     } else if ((c & 0xE0 ) == 0xC0) {
127         len = 2;
128                 c &= 0x1F;
129
130     } else if ((c & 0x80 ) == 0x80) {
131         return UCS4_INVALID;
132
133     } else {
134         return c;
135         }
136
137         for(i=1; i < len; i++) {
138                 ber_int_t ch = ((const unsigned char *) p)[i];
139
140                 if ((ch & 0xc0) != 0x80) {
141                         return UCS4_INVALID;
142                 }
143
144                 c <<= 6;
145                 c |= ch & 0x3f;
146         }
147
148         return c;
149 }
150
151 /* conv UCS-4 to UTF-8, not used */
152 int ldap_ucs4_to_utf8( ber_int_t c, char *buf )
153 {
154         int len=0;
155         unsigned char* p = buf;
156         if(buf == NULL) return 0;
157
158         if ( c < 0 ) {
159                 /* not a valid Unicode character */
160
161         } else if( c < 0x80 ) {
162                 p[len++] = c;
163
164         } else if( c < 0x800 ) {
165                 p[len++] = 0xc0 | ( c >> 6 );
166                 p[len++] = 0x80 | ( c & 0x3F );
167
168         } else if( c < 0x10000 ) {
169                 p[len++] = 0xe0 | ( c >> 12 );
170                 p[len++] = 0x80 | ( (c >> 6) & 0x3F );
171                 p[len++] = 0x80 | ( c & 0x3F );
172
173         } else if( c < 0x200000 ) {
174                 p[len++] = 0xf0 | ( c >> 18 );
175                 p[len++] = 0x80 | ( (c >> 12) & 0x3F );
176                 p[len++] = 0x80 | ( (c >> 6) & 0x3F );
177                 p[len++] = 0x80 | ( c & 0x3F );
178
179         } else if( c < 0x400000 ) {
180                 p[len++] = 0xf8 | ( c >> 24 );
181                 p[len++] = 0x80 | ( (c >> 18) & 0x3F );
182                 p[len++] = 0x80 | ( (c >> 12) & 0x3F );
183                 p[len++] = 0x80 | ( (c >> 6) & 0x3F );
184                 p[len++] = 0x80 | ( c & 0x3F );
185
186         } else /* if( c < 0x80000000 ) */ {
187                 p[len++] = 0xfc | ( c >> 30 );
188                 p[len++] = 0x80 | ( (c >> 24) & 0x3F );
189                 p[len++] = 0x80 | ( (c >> 18) & 0x3F );
190                 p[len++] = 0x80 | ( (c >> 12) & 0x3F );
191                 p[len++] = 0x80 | ( (c >> 6) & 0x3F );
192                 p[len++] = 0x80 | ( c & 0x3F );
193         }
194
195         buf[len] = '\0';
196         return len;
197 }
198
199 char* ldap_utf8_next( const char * p )
200 {
201         int len = ldap_utf8_charlen( p );
202
203         return len ? (char *) &p[len] : NULL;
204 }
205
206 char* ldap_utf8_prev( const char * p )
207 {
208         int i;
209         const unsigned char *u = p;
210
211         for( i = -1; i >= -6 ; i-- ) {
212                 if ( u[i] & 0xC0 != 0x80 ) return (char *) &p[i];
213         }
214
215         return NULL;
216 }
217
218 /*
219  * UTF-8 ctype routines
220  * Only deals with characters < 0x100 (ie: US-ASCII)
221  */
222
223 int ldap_utf8_isascii( const char * p )
224 {
225         unsigned c = * (const unsigned char *) p;
226         return UTF8_ISASCII(c);
227 }
228
229 int ldap_utf8_isdigit( const char * p )
230 {
231         unsigned c = * (const unsigned char *) p;
232
233         if(!UTF8_ISASCII(c)) return 0;
234
235         return c >= '0' && c <= '9';
236 }
237
238 int ldap_utf8_isxdigit( const char * p )
239 {
240         unsigned c = * (const unsigned char *) p;
241
242         if(!UTF8_ISASCII(c)) return 0;
243
244         return ( c >= '0' && c <= '9' )
245                 || ( c >= 'A' && c <= 'F' )
246                 || ( c >= 'a' && c <= 'f' );
247 }
248
249 int ldap_utf8_isspace( const char * p )
250 {
251         unsigned c = * (const unsigned char *) p;
252
253         if(!UTF8_ISASCII(c)) return 0;
254
255         switch(c) {
256         case ' ':
257         case '\t':
258         case '\n':
259         case '\r':
260         case '\v':
261         case '\f':
262                 return 1;
263         }
264
265         return 0;
266 }
267
268 #ifndef UTF8_ALPHA_CTYPE
269 /*
270  * These are not needed by the C SDK and are
271  * not "good enough" for general use.
272  */
273 int ldap_utf8_isalpha( const char * p )
274 {
275         unsigned c = * (const unsigned char *) p;
276
277         if(!UTF8_ISASCII(c)) return 0;
278
279         return ( c >= 'A' && c <= 'Z' )
280                 || ( c >= 'a' && c <= 'z' );
281 }
282
283 int ldap_utf8_isalnum( const char * p )
284 {
285         unsigned c = * (const unsigned char *) p;
286
287         if(!UTF8_ISASCII(c)) return 0;
288
289         return ( c >= '0' && c <= '9' )
290                 || ( c >= 'A' && c <= 'Z' )
291                 || ( c >= 'a' && c <= 'z' );
292 }
293
294 int ldap_utf8_islower( const char * p )
295 {
296         unsigned c = * (const unsigned char *) p;
297
298         if(!UTF8_ISASCII(c)) return 0;
299
300         return ( c >= 'a' && c <= 'z' );
301 }
302
303 int ldap_utf8_isupper( const char * p )
304 {
305         unsigned c = * (const unsigned char *) p;
306
307         if(!UTF8_ISASCII(c)) return 0;
308
309         return ( c >= 'A' && c <= 'Z' );
310 }
311 #endif
312
313 /*
314  * get one UTF-8 character
315  */
316 char* ldap_utf8_fgetc( FILE *s, char *buf )
317 {
318         int i;
319         unsigned char *p;
320         unsigned int c;
321         int len;
322
323         if( s == NULL ) return NULL;
324
325         p = buf;
326
327         c = fgetc( s );
328         if( c == EOF ) {
329                 p[0] = -1;
330                 return NULL;
331         }
332
333         p[0] = c;
334
335         len = ldap_utf8_charlen( buf );
336
337         if( len < 1 ) return NULL;
338
339         for( i = 1; i < len; i++ ) {
340                 unsigned int c = fgetc( s );
341                 if( c == EOF ) {
342                         p[i] = -1;
343                         return NULL;
344                 }
345                 if( c & 0xC0 != 0x80 ) {
346                         ungetc( c, s );
347                         p[i] = -1;
348                         return NULL;
349                 }
350                 p[i] = c;
351         }
352
353         return buf;
354 }
355
356
357 /*
358  * UTF-8 string routines
359  */
360
361 /* like strcspn() but returns number of bytes, not characters */
362 ber_len_t (ldap_utf8_strcspn)( const char *str, const char *set )
363 {
364         int len;
365         const char *cstr;
366
367         for( cstr = str; *cstr != '\0'; cstr += len ) {
368                 const char *cset;
369
370                 for( cset = set; ; cset += len ) {
371                         if( ldap_utf8_to_ucs4( cstr ) == ldap_utf8_to_ucs4( cset ) ) {
372                                 return cstr - str;
373                         } 
374
375                         len = ldap_utf8_charlen(cset);
376                         if( !len ) break;
377                 }
378
379                 len = ldap_utf8_charlen(cstr);
380                 if( !len ) break;
381         }
382
383         return cstr - str;
384 }
385
386 /* like strspn() but returns number of bytes, not characters */
387 ber_len_t (ldap_utf8_strspn)( const char *str, const char *set )
388 {
389         int len;
390         const char *cstr;
391
392         for( cstr = str; *cstr != '\0'; cstr += len ) {
393                 const char *cset;
394
395                 for( cset = set; ; cset += len ) {
396                         if( *cset == '\0' ) {
397                                 return cstr - str;
398                         }
399
400                         if( ldap_utf8_to_ucs4( cstr ) == ldap_utf8_to_ucs4( cset ) ) {
401                                 break;
402                         } 
403
404                         len = ldap_utf8_charlen(cset);
405                         if( !len ) break;
406                 }
407
408                 len = ldap_utf8_charlen(cstr);
409                 if( !len ) break;
410         }
411
412         return cstr - str;
413 }
414
415 /* like strpbrk(), replaces strchr() as well */
416 char *(ldap_utf8_strpbrk)( const char *str, const char *set )
417 {
418         int len;
419         const char *cstr;
420
421         for( cstr = str; *cstr != '\0'; cstr += len ) {
422                 const char *cset;
423
424                 for( cset = set; ; cset += len ) {
425                         if( ldap_utf8_to_ucs4( cstr ) == ldap_utf8_to_ucs4( cset ) ) {
426                                 return cstr;
427                         } 
428
429                         len = ldap_utf8_charlen(cset);
430                         if( !len ) break;
431                 }
432
433                 len = ldap_utf8_charlen(cstr);
434                 if( !len ) break;
435         }
436
437         return NULL;
438 }
439
440 /* like strtok_r(), not strtok() */
441 char *(ldap_utf8_strtok)(char *str, const char *sep, char **last)
442 {
443         char *begin;
444         char *end;
445
446         if( last == NULL ) return NULL;
447
448         begin = str ? str : *last;
449
450         begin += ldap_utf8_strspn( begin, sep );
451
452         if( *begin == '\0' ) {
453                 *last = NULL;
454                 return NULL;
455         }
456
457         end = &begin[ ldap_utf8_strcpn( begin, sep ) ];
458
459         if( *end != '\0' ) {
460                 int len = ldap_utf8_charlen( end );
461                 *end = '\0';
462
463                 if( len ) {
464                         end += len;
465                 } else {
466                         end = NULL;
467                 }
468         }
469
470         *last = end;
471         return begin;
472 }