2 * Copyright 2000-2002 The OpenLDAP Foundation
3 * COPYING RESTRICTIONS APPLY. See COPYRIGHT File in top level directory
4 * of this package for details.
10 #include <ac/string.h>
11 #include <ac/stdlib.h>
15 #include <ldap_utf8.h>
16 #include <ldap_pvt_uc.h>
18 #define malloc(x) ber_memalloc(x)
19 #define realloc(x,y) ber_memrealloc(x,y)
20 #define free(x) ber_memfree(x)
23 const ldap_unicode_t *u1,
24 const ldap_unicode_t *u2,
27 for(; 0 < n; ++u1, ++u2, --n ) {
29 return *u1 < *u2 ? -1 : +1;
39 const ldap_unicode_t *u1,
40 const ldap_unicode_t *u2,
43 for(; 0 < n; ++u1, ++u2, --n ) {
44 ldap_unicode_t uu1 = uctoupper( *u1 );
45 ldap_unicode_t uu2 = uctoupper( *u2 );
48 return uu1 < uu2 ? -1 : +1;
57 ldap_unicode_t * ucstrnchr(
58 const ldap_unicode_t *u,
62 for(; 0 < n; ++u, --n ) {
64 return (ldap_unicode_t *) u;
71 ldap_unicode_t * ucstrncasechr(
72 const ldap_unicode_t *u,
77 for(; 0 < n; ++u, --n ) {
78 if( uctoupper( *u ) == c ) {
79 return (ldap_unicode_t *) u;
90 for(; 0 < n; ++u, --n ) {
95 struct berval * UTF8bvnormalize(
100 int i, j, len, clen, outpos, ucsoutlen, outsize, last;
102 unsigned long *ucs, *p, *ucsout;
104 unsigned casefold = flags & LDAP_UTF8_CASEFOLD;
105 unsigned approx = flags & LDAP_UTF8_APPROX;
106 static unsigned char mask[] = {
107 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
117 return ber_dupbv( newbv, bv );
120 /* FIXME: Should first check to see if string is already in
121 * proper normalized form. This is almost as time consuming
122 * as the normalization though.
125 /* finish off everything up to character before first non-ascii */
126 if ( LDAP_UTF8_ISASCII( s ) ) {
129 out = (char *) malloc( outsize );
135 for ( i = 1; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
136 out[outpos++] = TOUPPER( s[i-1] );
139 out[outpos++] = TOUPPER( s[len - 1] );
141 return ber_str2bv( out, outpos, 0, newbv);
144 for ( i = 1; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
149 return ber_str2bv( s, len, 1, newbv );
153 out = (char *) malloc( outsize );
158 memcpy(out, s, outpos);
162 out = (char *) malloc( outsize );
170 p = ucs = (long *) malloc( len * sizeof(*ucs) );
176 /* convert character before first non-ascii to ucs-4 */
178 *p = casefold ? TOUPPER( s[i - 1] ) : s[i - 1];
182 /* s[i] is now first non-ascii character */
184 /* s[i] is non-ascii */
185 /* convert everything up to next ascii to ucs-4 */
187 clen = LDAP_UTF8_CHARLEN2( s + i, clen );
197 *p = s[i] & mask[clen];
199 for( j = 1; j < clen; j++ ) {
200 if ( (s[i] & 0xc0) != 0x80 ) {
210 *p = uctoupper( *p );
214 /* normalize ucs of length p - ucs */
215 uccanondecomp( ucs, p - ucs, &ucsout, &ucsoutlen );
217 for ( j = 0; j < ucsoutlen; j++ ) {
218 if ( ucsout[j] < 0x80 ) {
219 out[outpos++] = ucsout[j];
223 ucsoutlen = uccanoncomp( ucsout, ucsoutlen );
224 /* convert ucs to utf-8 and store in out */
225 for ( j = 0; j < ucsoutlen; j++ ) {
226 /* allocate more space if not enough room for
227 6 bytes and terminator */
228 if ( outsize - outpos < 7 ) {
229 outsize = ucsoutlen - j + outpos + 6;
230 out = (char *) realloc( out, outsize );
236 outpos += ldap_x_ucs4_to_utf8( ucsout[j], &out[outpos] );
247 /* finish off everything up to char before next non-ascii */
248 for ( i++; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
249 out[outpos++] = casefold ? TOUPPER( s[i-1] ) : s[i-1];
252 out[outpos++] = casefold ? TOUPPER( s[len - 1] ) : s[len - 1];
256 /* convert character before next non-ascii to ucs-4 */
257 *ucs = casefold ? TOUPPER( s[i - 1] ) : s[i - 1];
262 return ber_str2bv( out, outpos, 0, newbv );
265 /* compare UTF8-strings, optionally ignore casing */
266 /* slow, should be optimized */
272 int i, l1, l2, len, ulen, res;
273 char *s1, *s2, *done;
274 unsigned long *ucs, *ucsout1, *ucsout2;
275 unsigned casefold = flags & LDAP_UTF8_CASEFOLD;
276 unsigned norm1 = flags & LDAP_UTF8_ARG1NFC;
277 unsigned norm2 = flags & LDAP_UTF8_ARG2NFC;
280 return bv2 == NULL ? 0 : -1;
281 } else if (bv2 == NULL) {
288 len = (l1 < l2) ? l1 : l2;
290 return l1 == 0 ? (l2 == 0 ? 0 : -1) : 1;
297 while ( (s1 < done) && LDAP_UTF8_ISASCII(s1) && LDAP_UTF8_ISASCII(s2) ) {
299 char c1 = TOUPPER(*s1);
300 char c2 = TOUPPER(*s2);
308 /* done unless next character in s1 or s2 is non-ascii */
310 if (!LDAP_UTF8_ISASCII(s1) || !LDAP_UTF8_ISASCII(s2)) {
313 } else if ((len < l1) && !LDAP_UTF8_ISASCII(s1) ||
314 (len < l2) && !LDAP_UTF8_ISASCII(s2)) {
321 /* We have encountered non-ascii or strings equal up to len */
323 /* set i to number of iterations */
325 /* passed through loop at least once? */
327 if (!res && (s1 == done) &&
328 ((len == l1) || LDAP_UTF8_ISASCII(s1)) &&
329 ((len == l2) || LDAP_UTF8_ISASCII(s2))) {
330 /* all ascii and equal up to len */
334 /* rewind one char, and do normalized compare from there */
341 /* FIXME: Should first check to see if strings are already in
342 * proper normalized form.
345 ucs = (long *) malloc( ( ( norm1 || l1 > l2 ) ? l1 : l2 ) * sizeof(*ucs) );
347 return l1 > l2 ? 1 : -1; /* what to do??? */
351 * XXYYZ: we convert to ucs4 even though -llunicode
352 * expects ucs2 in an unsigned long
355 /* convert and normalize 1st string */
356 for ( i = 0, ulen = 0; i < l1; i += len, ulen++ ) {
357 ucs[ulen] = ldap_x_utf8_to_ucs4( s1 + i );
358 if ( ucs[ulen] == LDAP_UCS4_INVALID ) {
360 return -1; /* what to do??? */
362 len = LDAP_UTF8_CHARLEN( s1 + i );
368 ucs = (long *) malloc( l2 * sizeof(*ucs) );
370 return l1 > l2 ? 1 : -1; /* what to do??? */
373 uccanondecomp( ucs, ulen, &ucsout1, &l1 );
374 l1 = uccanoncomp( ucsout1, l1 );
377 /* convert and normalize 2nd string */
378 for ( i = 0, ulen = 0; i < l2; i += len, ulen++ ) {
379 ucs[ulen] = ldap_x_utf8_to_ucs4( s2 + i );
380 if ( ucs[ulen] == LDAP_UCS4_INVALID ) {
383 return 1; /* what to do??? */
385 len = LDAP_UTF8_CHARLEN( s2 + i );
392 uccanondecomp( ucs, ulen, &ucsout2, &l2 );
393 l2 = uccanoncomp( ucsout2, l2 );
398 ? ucstrncasecmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 )
399 : ucstrncmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 );
409 return l1 > l2 ? 1 : -1;