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 = uctolower( *u1 );
45 ldap_unicode_t uu2 = uctolower( *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( uctolower( *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;
101 char *out, *outtmp, *s;
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++] = TOLOWER( s[i-1] );
139 out[outpos++] = TOLOWER( 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 = malloc( len * sizeof(*ucs) );
176 /* convert character before first non-ascii to ucs-4 */
178 *p = casefold ? TOLOWER( 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 = uctolower( *p );
214 /* normalize ucs of length p - ucs */
215 uccompatdecomp( 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 outtmp = (char *) realloc( out, outsize );
231 if ( outtmp == NULL ) {
239 outpos += ldap_x_ucs4_to_utf8( ucsout[j], &out[outpos] );
253 /* finish off everything up to char before next non-ascii */
254 for ( i++; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
255 out[outpos++] = casefold ? TOLOWER( s[i-1] ) : s[i-1];
258 out[outpos++] = casefold ? TOLOWER( s[len - 1] ) : s[len - 1];
262 /* convert character before next non-ascii to ucs-4 */
263 *ucs = casefold ? TOLOWER( s[i - 1] ) : s[i - 1];
268 return ber_str2bv( out, outpos, 0, newbv );
271 /* compare UTF8-strings, optionally ignore casing */
272 /* slow, should be optimized */
278 int i, l1, l2, len, ulen, res = 0;
279 char *s1, *s2, *done;
280 unsigned long *ucs, *ucsout1, *ucsout2;
281 unsigned casefold = flags & LDAP_UTF8_CASEFOLD;
282 unsigned norm1 = flags & LDAP_UTF8_ARG1NFC;
283 unsigned norm2 = flags & LDAP_UTF8_ARG2NFC;
286 return bv2 == NULL ? 0 : -1;
287 } else if (bv2 == NULL) {
294 len = (l1 < l2) ? l1 : l2;
296 return l1 == 0 ? (l2 == 0 ? 0 : -1) : 1;
303 while ( (s1 < done) && LDAP_UTF8_ISASCII(s1) && LDAP_UTF8_ISASCII(s2) ) {
305 char c1 = TOLOWER(*s1);
306 char c2 = TOLOWER(*s2);
314 /* done unless next character in s1 or s2 is non-ascii */
316 if (!LDAP_UTF8_ISASCII(s1) || !LDAP_UTF8_ISASCII(s2)) {
319 } else if (((len < l1) && !LDAP_UTF8_ISASCII(s1)) ||
320 ((len < l2) && !LDAP_UTF8_ISASCII(s2))) {
327 /* We have encountered non-ascii or strings equal up to len */
329 /* set i to number of iterations */
331 /* passed through loop at least once? */
333 if (!res && (s1 == done) &&
334 ((len == l1) || LDAP_UTF8_ISASCII(s1)) &&
335 ((len == l2) || LDAP_UTF8_ISASCII(s2))) {
336 /* all ascii and equal up to len */
340 /* rewind one char, and do normalized compare from there */
347 /* FIXME: Should first check to see if strings are already in
348 * proper normalized form.
351 ucs = malloc( ( ( norm1 || l1 > l2 ) ? l1 : l2 ) * sizeof(*ucs) );
353 return l1 > l2 ? 1 : -1; /* what to do??? */
357 * XXYYZ: we convert to ucs4 even though -llunicode
358 * expects ucs2 in an unsigned long
361 /* convert and normalize 1st string */
362 for ( i = 0, ulen = 0; i < l1; i += len, ulen++ ) {
363 ucs[ulen] = ldap_x_utf8_to_ucs4( s1 + i );
364 if ( ucs[ulen] == LDAP_UCS4_INVALID ) {
366 return -1; /* what to do??? */
368 len = LDAP_UTF8_CHARLEN( s1 + i );
374 ucs = malloc( l2 * sizeof(*ucs) );
376 return l1 > l2 ? 1 : -1; /* what to do??? */
379 uccompatdecomp( ucs, ulen, &ucsout1, &l1 );
380 l1 = uccanoncomp( ucsout1, l1 );
383 /* convert and normalize 2nd string */
384 for ( i = 0, ulen = 0; i < l2; i += len, ulen++ ) {
385 ucs[ulen] = ldap_x_utf8_to_ucs4( s2 + i );
386 if ( ucs[ulen] == LDAP_UCS4_INVALID ) {
389 return 1; /* what to do??? */
391 len = LDAP_UTF8_CHARLEN( s2 + i );
398 uccompatdecomp( ucs, ulen, &ucsout2, &l2 );
399 l2 = uccanoncomp( ucsout2, l2 );
404 ? ucstrncasecmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 )
405 : ucstrncmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 );
415 return l1 > l2 ? 1 : -1;