#include "k5-platform.h"
#include "k5-utf8.h"
#include "supp-int.h"
#include "errno.h"
static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
static ssize_t
k5_utf8s_to_ucs2s(krb5_ucs2 *ucs2str,
const char *utf8str,
size_t count,
int little_endian)
{
size_t ucs2len = 0;
size_t utflen, i;
krb5_ucs2 ch;
if (utf8str == NULL || *utf8str == '\0') {
*ucs2str = 0;
return 0;
}
while (*utf8str && ucs2len < count) {
utflen = KRB5_UTF8_CHARLEN2(utf8str, utflen);
if (utflen == 0 || utflen > KRB5_MAX_UTF8_LEN)
return -1;
ch = (krb5_ucs2)(utf8str[0] & mask[utflen]);
for (i = 1; i < utflen; i++) {
if ((utf8str[i] & 0xc0) != 0x80)
return -1;
ch <<= 6;
ch |= (krb5_ucs2)(utf8str[i] & 0x3f);
}
if (ucs2str != NULL) {
#ifdef K5_BE
#ifndef SWAP16
#define SWAP16(X) ((((X) << 8) | ((X) >> 8)) & 0xFFFF)
#endif
if (little_endian)
ucs2str[ucs2len] = SWAP16(ch);
else
#endif
ucs2str[ucs2len] = ch;
}
utf8str += utflen;
ucs2len++;
}
assert(ucs2len < count);
if (ucs2str != NULL) {
ucs2str[ucs2len] = 0;
}
return ucs2len;
}
int
krb5int_utf8s_to_ucs2s(const char *utf8s,
krb5_ucs2 **ucs2s,
size_t *ucs2chars)
{
ssize_t len;
size_t chars;
chars = krb5int_utf8_chars(utf8s);
*ucs2s = (krb5_ucs2 *)malloc((chars + 1) * sizeof(krb5_ucs2));
if (*ucs2s == NULL) {
return ENOMEM;
}
len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars + 1, 0);
if (len < 0) {
free(*ucs2s);
*ucs2s = NULL;
return EINVAL;
}
if (ucs2chars != NULL) {
*ucs2chars = chars;
}
return 0;
}
int
krb5int_utf8cs_to_ucs2s(const char *utf8s,
size_t utf8slen,
krb5_ucs2 **ucs2s,
size_t *ucs2chars)
{
ssize_t len;
size_t chars;
chars = krb5int_utf8c_chars(utf8s, utf8slen);
*ucs2s = (krb5_ucs2 *)malloc((chars + 1) * sizeof(krb5_ucs2));
if (*ucs2s == NULL) {
return ENOMEM;
}
len = k5_utf8s_to_ucs2s(*ucs2s, utf8s, chars + 1, 0);
if (len < 0) {
free(*ucs2s);
*ucs2s = NULL;
return EINVAL;
}
if (ucs2chars != NULL) {
*ucs2chars = chars;
}
return 0;
}
int
krb5int_utf8s_to_ucs2les(const char *utf8s,
unsigned char **ucs2les,
size_t *ucs2leslen)
{
ssize_t len;
size_t chars;
chars = krb5int_utf8_chars(utf8s);
*ucs2les = (unsigned char *)malloc((chars + 1) * sizeof(krb5_ucs2));
if (*ucs2les == NULL) {
return ENOMEM;
}
len = k5_utf8s_to_ucs2s((krb5_ucs2 *)*ucs2les, utf8s, chars + 1, 1);
if (len < 0) {
free(*ucs2les);
*ucs2les = NULL;
return EINVAL;
}
if (ucs2leslen != NULL) {
*ucs2leslen = chars * sizeof(krb5_ucs2);
}
return 0;
}
int
krb5int_utf8cs_to_ucs2les(const char *utf8s,
size_t utf8slen,
unsigned char **ucs2les,
size_t *ucs2leslen)
{
ssize_t len;
size_t chars;
chars = krb5int_utf8c_chars(utf8s, utf8slen);
*ucs2les = (unsigned char *)malloc((chars + 1) * sizeof(krb5_ucs2));
if (*ucs2les == NULL) {
return ENOMEM;
}
len = k5_utf8s_to_ucs2s((krb5_ucs2 *)*ucs2les, utf8s, chars + 1, 1);
if (len < 0) {
free(*ucs2les);
*ucs2les = NULL;
return EINVAL;
}
if (ucs2leslen != NULL) {
*ucs2leslen = chars * sizeof(krb5_ucs2);
}
return 0;
}
static ssize_t
k5_ucs2s_to_utf8s(char *utf8str, const krb5_ucs2 *ucs2str,
size_t count, ssize_t ucs2len, int little_endian)
{
int len = 0;
int n;
char *p = utf8str;
krb5_ucs2 empty = 0, ch;
if (ucs2str == NULL)
ucs2str = ∅
if (utf8str == NULL)
{
while (ucs2len == -1 ? *ucs2str : --ucs2len >= 0) {
ch = *ucs2str++;
#ifdef K5_BE
if (little_endian)
ch = SWAP16(ch);
#endif
n = krb5int_ucs2_to_utf8(ch, NULL);
if (n < 1)
return -1;
if (len + n < len)
return -1;
len += n;
}
return len;
}
n = 1;
while (ucs2len == -1 ? *ucs2str != 0 : --ucs2len >= 0) {
ch = *ucs2str++;
#ifdef K5_BE
if (little_endian)
ch = SWAP16(ch);
#endif
n = krb5int_ucs2_to_utf8(ch, p);
if (n < 1)
break;
p += n;
count -= n;
}
if (n == 0) {
while (count--)
*p++ = 0;
}
else if (count)
*p = 0;
if (n == -1)
return -1;
return (p - utf8str);
}
int
krb5int_ucs2s_to_utf8s(const krb5_ucs2 *ucs2s,
char **utf8s,
size_t *utf8slen)
{
ssize_t len;
len = k5_ucs2s_to_utf8s(NULL, ucs2s, 0, -1, 0);
if (len < 0) {
return EINVAL;
}
*utf8s = (char *)malloc((size_t)len + 1);
if (*utf8s == NULL) {
return ENOMEM;
}
len = k5_ucs2s_to_utf8s(*utf8s, ucs2s, (size_t)len + 1, -1, 0);
if (len < 0) {
free(*utf8s);
*utf8s = NULL;
return EINVAL;
}
if (utf8slen != NULL) {
*utf8slen = len;
}
return 0;
}
int
krb5int_ucs2les_to_utf8s(const unsigned char *ucs2les,
char **utf8s,
size_t *utf8slen)
{
ssize_t len;
len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2les, 0, -1, 1);
if (len < 0)
return EINVAL;
*utf8s = (char *)malloc((size_t)len + 1);
if (*utf8s == NULL) {
return ENOMEM;
}
len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les, (size_t)len + 1, -1, 1);
if (len < 0) {
free(*utf8s);
*utf8s = NULL;
return EINVAL;
}
if (utf8slen != NULL) {
*utf8slen = len;
}
return 0;
}
int
krb5int_ucs2cs_to_utf8s(const krb5_ucs2 *ucs2s,
size_t ucs2slen,
char **utf8s,
size_t *utf8slen)
{
ssize_t len;
if (ucs2slen > SSIZE_MAX)
return ERANGE;
len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2s, 0,
(ssize_t)ucs2slen, 0);
if (len < 0)
return EINVAL;
*utf8s = (char *)malloc((size_t)len + 1);
if (*utf8s == NULL) {
return ENOMEM;
}
len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2s,
(size_t)len + 1, (ssize_t)ucs2slen, 0);
if (len < 0) {
free(*utf8s);
*utf8s = NULL;
return EINVAL;
}
if (utf8slen != NULL) {
*utf8slen = len;
}
return 0;
}
int
krb5int_ucs2lecs_to_utf8s(const unsigned char *ucs2les,
size_t ucs2leslen,
char **utf8s,
size_t *utf8slen)
{
ssize_t len;
if (ucs2leslen > SSIZE_MAX)
return ERANGE;
len = k5_ucs2s_to_utf8s(NULL, (krb5_ucs2 *)ucs2les, 0,
(ssize_t)ucs2leslen, 1);
if (len < 0)
return EINVAL;
*utf8s = (char *)malloc((size_t)len + 1);
if (*utf8s == NULL) {
return ENOMEM;
}
len = k5_ucs2s_to_utf8s(*utf8s, (krb5_ucs2 *)ucs2les,
(size_t)len + 1, (ssize_t)ucs2leslen, 1);
if (len < 0) {
free(*utf8s);
*utf8s = NULL;
return EINVAL;
}
if (utf8slen != NULL) {
*utf8slen = len;
}
return 0;
}