/** * FreeRDP: A Remote Desktop Protocol Client * Unicode Utils * * Copyright 2011 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include /* Convert pin/in_len from WINDOWS_CODEPAGE - return like xstrdup, 0-terminated */ char* freerdp_uniconv_in(UNICONV* uniconv, unsigned char* pin, size_t in_len) { unsigned char *conv_pin = pin; size_t conv_in_len = in_len; char *pout = xmalloc(in_len * 2 + 1), *conv_pout = pout; size_t conv_out_len = in_len * 2; #ifdef HAVE_ICONV if (iconv(uniconv->in_iconv_h, (ICONV_CONST char **) &conv_pin, &conv_in_len, &conv_pout, &conv_out_len) == (size_t) - 1) { /* TODO: xrealloc if conv_out_len == 0 - it shouldn't be needed, but would allow a smaller initial alloc ... */ printf("freerdp_uniconv_in: iconv failure\n"); return 0; } #else while (conv_in_len >= 2) { unsigned int wc; wc = (unsigned int)(unsigned char)(*conv_pin++); wc += ((unsigned int)(unsigned char)(*conv_pin++)) << 8; conv_in_len -= 2; if (wc >= 0xD800 && wc <= 0xDFFF && conv_in_len >= 2) { /* Code points U+10000 to U+10FFFF using surrogate pair */ wc = ((wc - 0xD800) << 10) + 0x10000; wc += (unsigned int)(unsigned char)(*conv_pin++); wc += ((unsigned int)(unsigned char)(*conv_pin++) - 0xDC) << 8; conv_in_len -= 2; } if (wc <= 0x7F) { *conv_pout++ = (char)wc; conv_out_len--; } else if (wc <= 0x07FF) { *conv_pout++ = (char)(0xC0 + (wc >> 6)); *conv_pout++ = (char)(0x80 + (wc & 0x3F)); conv_out_len -= 2; } else if (wc <= 0xFFFF) { *conv_pout++ = (char)(0xE0 + (wc >> 12)); *conv_pout++ = (char)(0x80 + ((wc >> 6) & 0x3F)); *conv_pout++ = (char)(0x80 + (wc & 0x3F)); conv_out_len -= 3; } else { *conv_pout++ = (char)(0xF0 + (wc >> 18)); *conv_pout++ = (char)(0x80 + ((wc >> 12) & 0x3F)); *conv_pout++ = (char)(0x80 + ((wc >> 6) & 0x3F)); *conv_pout++ = (char)(0x80 + (wc & 0x3F)); conv_out_len -= 4; } } #endif if (conv_in_len > 0) { printf("freerdp_uniconv_in: conversion failure - %d chars left\n", (int) conv_in_len); } *conv_pout = 0; return pout; } /* Convert str from DEFAULT_CODEPAGE to WINDOWS_CODEPAGE and return buffer like xstrdup. * Buffer is 0-terminated but that is not included in the returned length. */ char* freerdp_uniconv_out(UNICONV *uniconv, char *str, size_t *pout_len) { size_t ibl; size_t obl; char* pin; char* pout; char* pout0; if (str == NULL) { *pout_len = 0; return NULL; } ibl = strlen(str); obl = 2 * ibl; pin = str; pout0 = xmalloc(obl + 2); pout = pout0; #ifdef HAVE_ICONV if (iconv(uniconv->out_iconv_h, (ICONV_CONST char **) &pin, &ibl, &pout, &obl) == (size_t) - 1) { printf("freerdp_uniconv_out: iconv() error\n"); return NULL; } #else while ((ibl > 0) && (obl > 0)) { unsigned int wc; wc = (unsigned int)(unsigned char)(*pin++); ibl--; if (wc >= 0xF0) { wc = (wc - 0xF0) << 18; wc += ((unsigned int)(unsigned char)(*pin++) - 0x80) << 12; wc += ((unsigned int)(unsigned char)(*pin++) - 0x80) << 6; wc += ((unsigned int)(unsigned char)(*pin++) - 0x80); ibl -= 3; } else if (wc >= 0xE0) { wc = (wc - 0xE0) << 12; wc += ((unsigned int)(unsigned char)(*pin++) - 0x80) << 6; wc += ((unsigned int)(unsigned char)(*pin++) - 0x80); ibl -= 2; } else if (wc >= 0xC0) { wc = (wc - 0xC0) << 6; wc += ((unsigned int)(unsigned char)(*pin++) - 0x80); ibl -= 1; } if (wc <= 0xFFFF) { *pout++ = (char)(wc & 0xFF); *pout++ = (char)(wc >> 8); obl -= 2; } else { wc -= 0x10000; *pout++ = (char)((wc >> 10) & 0xFF); *pout++ = (char)((wc >> 18) + 0xD8); *pout++ = (char)(wc & 0xFF); *pout++ = (char)(((wc >> 8) & 0x03) + 0xDC); obl -= 4; } } #endif if (ibl > 0) { printf("freerdp_uniconv_out: string not fully converted - %d chars left\n", (int) ibl); } *pout_len = pout - pout0; *pout++ = 0; /* Add extra double zero termination */ *pout = 0; return pout0; } /* Uppercase a unicode string */ void freerdp_uniconv_uppercase(UNICONV *uniconv, char *wstr, int length) { int i; unsigned char* p; unsigned int wc, uwc; p = (unsigned char*)wstr; for (i = 0; i < length; i++) { wc = (unsigned int)(*p); wc += (unsigned int)(*(p + 1)) << 8; uwc = towupper(wc); if (uwc != wc) { *p = uwc & 0xFF; *(p + 1) = (uwc >> 8) & 0xFF; } p += 2; } } UNICONV* freerdp_uniconv_new() { UNICONV *uniconv = xnew(UNICONV); #ifdef HAVE_ICONV uniconv->iconv = 1; uniconv->in_iconv_h = iconv_open(DEFAULT_CODEPAGE, WINDOWS_CODEPAGE); if (errno == EINVAL) { printf("Error opening iconv converter to %s from %s\n", DEFAULT_CODEPAGE, WINDOWS_CODEPAGE); } uniconv->out_iconv_h = iconv_open(WINDOWS_CODEPAGE, DEFAULT_CODEPAGE); if (errno == EINVAL) { printf("Error opening iconv converter to %s from %s\n", WINDOWS_CODEPAGE, DEFAULT_CODEPAGE); } #endif return uniconv; } void freerdp_uniconv_free(UNICONV *uniconv) { if (uniconv != NULL) { #ifdef HAVE_ICONV iconv_close(uniconv->in_iconv_h); iconv_close(uniconv->out_iconv_h); #endif xfree(uniconv); } }