2N/A/*
2N/A * $OpenLDAP: pkg/ldap/libraries/liblunicode/ucstr.c,v 1.40 2008/03/04
2N/A * 06:24:05 hyc Exp $
2N/A */
2N/A/*
2N/A * This work is part of OpenLDAP Software <http://www.openldap.org/>.
2N/A *
2N/A * Copyright 1998-2008 The OpenLDAP Foundation. All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted only as authorized by the OpenLDAP Public
2N/A * License.
2N/A *
2N/A * A copy of this license is available in file LICENSE in the top-level
2N/A * directory of the distribution or, alternatively, at
2N/A * <http://www.OpenLDAP.org/license.html>.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A#include "k5-utf8.h"
2N/A#include "k5-unicode.h"
2N/A/* Solaris Kerberos */
2N/A#include "ucdata.h"
2N/A
2N/A#include <ctype.h>
2N/A
2N/Aint
2N/Akrb5int_ucstrncmp(
2N/A const krb5_unicode * u1,
2N/A const krb5_unicode * u2,
2N/A size_t n)
2N/A{
2N/A for (; 0 < n; ++u1, ++u2, --n) {
2N/A if (*u1 != *u2) {
2N/A return *u1 < *u2 ? -1 : +1;
2N/A }
2N/A if (*u1 == 0) {
2N/A return 0;
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Aint
2N/Akrb5int_ucstrncasecmp(
2N/A const krb5_unicode * u1,
2N/A const krb5_unicode * u2,
2N/A size_t n)
2N/A{
2N/A for (; 0 < n; ++u1, ++u2, --n) {
2N/A krb5_unicode uu1 = uctolower(*u1);
2N/A krb5_unicode uu2 = uctolower(*u2);
2N/A
2N/A if (uu1 != uu2) {
2N/A return uu1 < uu2 ? -1 : +1;
2N/A }
2N/A if (uu1 == 0) {
2N/A return 0;
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_unicode *
2N/Akrb5int_ucstrnchr(
2N/A const krb5_unicode * u,
2N/A size_t n,
2N/A krb5_unicode c)
2N/A{
2N/A for (; 0 < n; ++u, --n) {
2N/A if (*u == c) {
2N/A return (krb5_unicode *) u;
2N/A }
2N/A }
2N/A
2N/A return NULL;
2N/A}
2N/A
2N/Akrb5_unicode *
2N/Akrb5int_ucstrncasechr(
2N/A const krb5_unicode * u,
2N/A size_t n,
2N/A krb5_unicode c)
2N/A{
2N/A c = uctolower(c);
2N/A for (; 0 < n; ++u, --n) {
2N/A if ((krb5_unicode) uctolower(*u) == c) {
2N/A return (krb5_unicode *) u;
2N/A }
2N/A }
2N/A
2N/A return NULL;
2N/A}
2N/A
2N/Avoid
2N/Akrb5int_ucstr2upper(
2N/A krb5_unicode * u,
2N/A size_t n)
2N/A{
2N/A for (; 0 < n; ++u, --n) {
2N/A *u = uctoupper(*u);
2N/A }
2N/A}
2N/A
2N/A#define TOUPPER(c) (islower(c) ? toupper(c) : (c))
2N/A#define TOLOWER(c) (isupper(c) ? tolower(c) : (c))
2N/A
2N/Akrb5_error_code
2N/Akrb5int_utf8_normalize(
2N/A krb5_data * data,
2N/A krb5_data ** newdataptr,
2N/A unsigned flags)
2N/A{
2N/A int i, j, len, clen, outpos, ucsoutlen, outsize, last;
2N/A char *out = NULL, *outtmp, *s;
2N/A krb5_ucs4 *ucs = NULL, *p, *ucsout = NULL;
2N/A krb5_data *newdata;
2N/A krb5_error_code retval = 0;
2N/A
2N/A static unsigned char mask[] = {
2N/A 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
2N/A
2N/A unsigned casefold = flags & KRB5_UTF8_CASEFOLD;
2N/A unsigned approx = flags & KRB5_UTF8_APPROX;
2N/A
2N/A *newdataptr = NULL;
2N/A
2N/A s = data->data;
2N/A len = data->length;
2N/A
2N/A newdata = malloc(sizeof(*newdata));
2N/A if (newdata == NULL)
2N/A return ENOMEM;
2N/A
2N/A /*
2N/A * Should first check to see if string is already in proper normalized
2N/A * form. This is almost as time consuming as the normalization though.
2N/A */
2N/A
2N/A /* finish off everything up to character before first non-ascii */
2N/A if (KRB5_UTF8_ISASCII(s)) {
2N/A if (casefold) {
2N/A outsize = len + 7;
2N/A out = malloc(outsize);
2N/A if (out == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A outpos = 0;
2N/A
2N/A for (i = 1; (i < len) && KRB5_UTF8_ISASCII(s + i); i++) {
2N/A out[outpos++] = TOLOWER(s[i - 1]);
2N/A }
2N/A if (i == len) {
2N/A out[outpos++] = TOLOWER(s[len - 1]);
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A for (i = 1; (i < len) && KRB5_UTF8_ISASCII(s + i); i++) {
2N/A /* empty */
2N/A }
2N/A
2N/A if (i == len) {
2N/A newdata->length = len;
2N/A newdata->data = malloc(newdata->length + 1);
2N/A if (newdata->data == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A memcpy(newdata->data, s, len);
2N/A newdata->data[len] = '\0';
2N/A *newdataptr = newdata;
2N/A return 0;
2N/A }
2N/A outsize = len + 7;
2N/A out = malloc(outsize);
2N/A if (out == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A outpos = i - 1;
2N/A memcpy(out, s, outpos);
2N/A }
2N/A } else {
2N/A outsize = len + 7;
2N/A out = malloc(outsize);
2N/A if (out == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A outpos = 0;
2N/A i = 0;
2N/A }
2N/A
2N/A p = ucs = malloc(len * sizeof(*ucs));
2N/A if (ucs == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A /* convert character before first non-ascii to ucs-4 */
2N/A if (i > 0) {
2N/A *p = casefold ? TOLOWER(s[i - 1]) : s[i - 1];
2N/A p++;
2N/A }
2N/A /* s[i] is now first non-ascii character */
2N/A for (;;) {
2N/A /* s[i] is non-ascii */
2N/A /* convert everything up to next ascii to ucs-4 */
2N/A while (i < len) {
2N/A clen = KRB5_UTF8_CHARLEN2(s + i, clen);
2N/A if (clen == 0) {
2N/A retval = KRB5_ERR_INVALID_UTF8;
2N/A goto cleanup;
2N/A }
2N/A if (clen == 1) {
2N/A /* ascii */
2N/A break;
2N/A }
2N/A *p = s[i] & mask[clen];
2N/A i++;
2N/A for (j = 1; j < clen; j++) {
2N/A if ((s[i] & 0xc0) != 0x80) {
2N/A retval = KRB5_ERR_INVALID_UTF8;
2N/A goto cleanup;
2N/A }
2N/A *p <<= 6;
2N/A *p |= s[i] & 0x3f;
2N/A i++;
2N/A }
2N/A if (casefold) {
2N/A *p = uctolower(*p);
2N/A }
2N/A p++;
2N/A }
2N/A /* normalize ucs of length p - ucs */
2N/A /* Solaris Kerberos */
2N/A uccompatdecomp((const krb5_ui_4 *)ucs, p - ucs,
2N/A (krb5_ui_4 **)&ucsout, &ucsoutlen);
2N/A if (approx) {
2N/A for (j = 0; j < ucsoutlen; j++) {
2N/A if (ucsout[j] < 0x80) {
2N/A out[outpos++] = ucsout[j];
2N/A }
2N/A }
2N/A } else {
2N/A /* Solaris Kerberos */
2N/A ucsoutlen = uccanoncomp((krb5_ui_4 *)ucsout, ucsoutlen);
2N/A /* convert ucs to utf-8 and store in out */
2N/A for (j = 0; j < ucsoutlen; j++) {
2N/A /*
2N/A * allocate more space if not enough room for 6 bytes and
2N/A * terminator
2N/A */
2N/A if (outsize - outpos < 7) {
2N/A outsize = ucsoutlen - j + outpos + 6;
2N/A outtmp = realloc(out, outsize);
2N/A if (outtmp == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A out = outtmp;
2N/A }
2N/A outpos += krb5int_ucs4_to_utf8(ucsout[j], &out[outpos]);
2N/A }
2N/A }
2N/A
2N/A free(ucsout);
2N/A ucsout = NULL;
2N/A
2N/A if (i == len) {
2N/A break;
2N/A }
2N/A last = i;
2N/A
2N/A /* Allocate more space in out if necessary */
2N/A if (len - i >= outsize - outpos) {
2N/A outsize += 1 + ((len - i) - (outsize - outpos));
2N/A outtmp = realloc(out, outsize);
2N/A if (outtmp == NULL) {
2N/A retval = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A out = outtmp;
2N/A }
2N/A /* s[i] is ascii */
2N/A /* finish off everything up to char before next non-ascii */
2N/A for (i++; (i < len) && KRB5_UTF8_ISASCII(s + i); i++) {
2N/A out[outpos++] = casefold ? TOLOWER(s[i - 1]) : s[i - 1];
2N/A }
2N/A if (i == len) {
2N/A out[outpos++] = casefold ? TOLOWER(s[len - 1]) : s[len - 1];
2N/A break;
2N/A }
2N/A /* convert character before next non-ascii to ucs-4 */
2N/A *ucs = casefold ? TOLOWER(s[i - 1]) : s[i - 1];
2N/A p = ucs + 1;
2N/A }
2N/A
2N/Acleanup:
2N/A free(ucs);
2N/A free(ucsout);
2N/A if (retval) {
2N/A free(out);
2N/A free(newdata);
2N/A return retval;
2N/A }
2N/A out[outpos] = '\0';
2N/A newdata->data = out;
2N/A newdata->length = outpos;
2N/A *newdataptr = newdata;
2N/A return 0;
2N/A}
2N/A
2N/A/* compare UTF8-strings, optionally ignore casing */
2N/A/* slow, should be optimized */
2N/Aint
2N/Akrb5int_utf8_normcmp(
2N/A const krb5_data * data1,
2N/A const krb5_data * data2,
2N/A unsigned flags)
2N/A{
2N/A int i, l1, l2, len, ulen, res = 0;
2N/A char *s1, *s2, *done;
2N/A krb5_ucs4 *ucs, *ucsout1, *ucsout2;
2N/A
2N/A unsigned casefold = flags & KRB5_UTF8_CASEFOLD;
2N/A unsigned norm1 = flags & KRB5_UTF8_ARG1NFC;
2N/A unsigned norm2 = flags & KRB5_UTF8_ARG2NFC;
2N/A
2N/A if (data1 == NULL) {
2N/A return data2 == NULL ? 0 : -1;
2N/A
2N/A } else if (data2 == NULL) {
2N/A return 1;
2N/A }
2N/A l1 = data1->length;
2N/A l2 = data2->length;
2N/A
2N/A len = (l1 < l2) ? l1 : l2;
2N/A if (len == 0) {
2N/A return l1 == 0 ? (l2 == 0 ? 0 : -1) : 1;
2N/A }
2N/A s1 = data1->data;
2N/A s2 = data2->data;
2N/A done = s1 + len;
2N/A
2N/A while ((s1 < done) && KRB5_UTF8_ISASCII(s1) && KRB5_UTF8_ISASCII(s2)) {
2N/A if (casefold) {
2N/A char c1 = TOLOWER(*s1);
2N/A char c2 = TOLOWER(*s2);
2N/A res = c1 - c2;
2N/A } else {
2N/A res = *s1 - *s2;
2N/A }
2N/A s1++;
2N/A s2++;
2N/A if (res) {
2N/A /* done unless next character in s1 or s2 is non-ascii */
2N/A if (s1 < done) {
2N/A if (!KRB5_UTF8_ISASCII(s1) || !KRB5_UTF8_ISASCII(s2)) {
2N/A break;
2N/A }
2N/A } else if (((len < l1) && !KRB5_UTF8_ISASCII(s1)) ||
2N/A ((len < l2) && !KRB5_UTF8_ISASCII(s2))) {
2N/A break;
2N/A }
2N/A return res;
2N/A }
2N/A }
2N/A
2N/A /* We have encountered non-ascii or strings equal up to len */
2N/A
2N/A /* set i to number of iterations */
2N/A i = s1 - done + len;
2N/A /* passed through loop at least once? */
2N/A if (i > 0) {
2N/A if (!res && (s1 == done) &&
2N/A ((len == l1) || KRB5_UTF8_ISASCII(s1)) &&
2N/A ((len == l2) || KRB5_UTF8_ISASCII(s2))) {
2N/A /* all ascii and equal up to len */
2N/A return l1 - l2;
2N/A }
2N/A /* rewind one char, and do normalized compare from there */
2N/A s1--;
2N/A s2--;
2N/A l1 -= i - 1;
2N/A l2 -= i - 1;
2N/A }
2N/A /*
2N/A * Should first check to see if strings are already in proper normalized
2N/A * form.
2N/A */
2N/A ucs = malloc(((norm1 || l1 > l2) ? l1 : l2) * sizeof(*ucs));
2N/A if (ucs == NULL) {
2N/A return l1 > l2 ? 1 : -1;/* what to do??? */
2N/A }
2N/A /*
2N/A * XXYYZ: we convert to ucs4 even though -llunicode
2N/A * expects ucs2 in an ac_uint4
2N/A */
2N/A
2N/A /* convert and normalize 1st string */
2N/A for (i = 0, ulen = 0; i < l1; i += len, ulen++) {
2N/A if (krb5int_utf8_to_ucs4(s1 + i, &ucs[ulen]) == -1) {
2N/A free(ucs);
2N/A return -1; /* what to do??? */
2N/A }
2N/A len = KRB5_UTF8_CHARLEN(s1 + i);
2N/A }
2N/A
2N/A if (norm1) {
2N/A ucsout1 = ucs;
2N/A l1 = ulen;
2N/A ucs = malloc(l2 * sizeof(*ucs));
2N/A if (ucs == NULL) {
2N/A free(ucsout1);
2N/A return l1 > l2 ? 1 : -1; /* what to do??? */
2N/A }
2N/A } else {
2N/A /* Solaris Kerberos */
2N/A uccompatdecomp((const krb5_ui_4 *)ucs, ulen, (krb5_ui_4 **)&ucsout1, &l1);
2N/A l1 = uccanoncomp((krb5_ui_4 *)ucsout1, l1);
2N/A }
2N/A
2N/A /* convert and normalize 2nd string */
2N/A for (i = 0, ulen = 0; i < l2; i += len, ulen++) {
2N/A if (krb5int_utf8_to_ucs4(s2 + i, &ucs[ulen]) == -1) {
2N/A free(ucsout1);
2N/A free(ucs);
2N/A return 1; /* what to do??? */
2N/A }
2N/A len = KRB5_UTF8_CHARLEN(s2 + i);
2N/A }
2N/A
2N/A if (norm2) {
2N/A ucsout2 = ucs;
2N/A l2 = ulen;
2N/A } else {
2N/A /* Solaris Kerberos */
2N/A uccompatdecomp((const krb5_ui_4 *)ucs, ulen, (krb5_ui_4 **)&ucsout2, &l2);
2N/A l2 = uccanoncomp((krb5_ui_4 *)ucsout2, l2);
2N/A free(ucs);
2N/A }
2N/A
2N/A res = casefold
2N/A ? krb5int_ucstrncasecmp(ucsout1, ucsout2, l1 < l2 ? l1 : l2)
2N/A : krb5int_ucstrncmp(ucsout1, ucsout2, l1 < l2 ? l1 : l2);
2N/A free(ucsout1);
2N/A free(ucsout2);
2N/A
2N/A if (res != 0) {
2N/A return res;
2N/A }
2N/A if (l1 == l2) {
2N/A return 0;
2N/A }
2N/A return l1 > l2 ? 1 : -1;
2N/A}