Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / libfreerdp-utils / unicode.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * Unicode Utils
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <errno.h>
21 #include <wctype.h>
22 #include <freerdp/utils/memory.h>
23
24 #include <freerdp/utils/unicode.h>
25
26 /* Convert pin/in_len from WINDOWS_CODEPAGE - return like xstrdup, 0-terminated */
27
28 char* freerdp_uniconv_in(UNICONV* uniconv, unsigned char* pin, size_t in_len)
29 {
30         unsigned char *conv_pin = pin;
31         size_t conv_in_len = in_len;
32         char *pout = xmalloc(in_len * 2 + 1), *conv_pout = pout;
33         size_t conv_out_len = in_len * 2;
34
35 #ifdef HAVE_ICONV
36         if (iconv(uniconv->in_iconv_h, (ICONV_CONST char **) &conv_pin, &conv_in_len, &conv_pout, &conv_out_len) == (size_t) - 1)
37         {
38                 /* TODO: xrealloc if conv_out_len == 0 - it shouldn't be needed, but would allow a smaller initial alloc ... */
39                 printf("freerdp_uniconv_in: iconv failure\n");
40                 return 0;
41         }
42 #else
43         while (conv_in_len >= 2)
44         {
45                 unsigned int wc;
46
47                 wc = (unsigned int)(unsigned char)(*conv_pin++);
48                 wc += ((unsigned int)(unsigned char)(*conv_pin++)) << 8;
49                 conv_in_len -= 2;
50
51                 if (wc >= 0xD800 && wc <= 0xDFFF && conv_in_len >= 2)
52                 {
53                         /* Code points U+10000 to U+10FFFF using surrogate pair */
54                         wc = ((wc - 0xD800) << 10) + 0x10000;
55                         wc += (unsigned int)(unsigned char)(*conv_pin++);
56                         wc += ((unsigned int)(unsigned char)(*conv_pin++) - 0xDC) << 8;
57                         conv_in_len -= 2;
58                 }
59
60                 if (wc <= 0x7F)
61                 {
62                         *conv_pout++ = (char)wc;
63                         conv_out_len--;
64                 }
65                 else if (wc <= 0x07FF)
66                 {
67                         *conv_pout++ = (char)(0xC0 + (wc >> 6));
68                         *conv_pout++ = (char)(0x80 + (wc & 0x3F));
69                         conv_out_len -= 2;
70                 }
71                 else if (wc <= 0xFFFF)
72                 {
73                         *conv_pout++ = (char)(0xE0 + (wc >> 12));
74                         *conv_pout++ = (char)(0x80 + ((wc >> 6) & 0x3F));
75                         *conv_pout++ = (char)(0x80 + (wc & 0x3F));
76                         conv_out_len -= 3;
77                 }
78                 else
79                 {
80                         *conv_pout++ = (char)(0xF0 + (wc >> 18));
81                         *conv_pout++ = (char)(0x80 + ((wc >> 12) & 0x3F));
82                         *conv_pout++ = (char)(0x80 + ((wc >> 6) & 0x3F));
83                         *conv_pout++ = (char)(0x80 + (wc & 0x3F));
84                         conv_out_len -= 4;
85                 }
86         }
87 #endif
88
89         if (conv_in_len > 0)
90         {
91                 printf("freerdp_uniconv_in: conversion failure - %d chars left\n", (int) conv_in_len);
92         }
93
94         *conv_pout = 0;
95         return pout;
96 }
97
98 /* Convert str from DEFAULT_CODEPAGE to WINDOWS_CODEPAGE and return buffer like xstrdup.
99  * Buffer is 0-terminated but that is not included in the returned length. */
100
101 char* freerdp_uniconv_out(UNICONV *uniconv, char *str, size_t *pout_len)
102 {
103         size_t ibl;
104         size_t obl;
105         char* pin;
106         char* pout;
107         char* pout0;
108
109         if (str == NULL)
110         {
111                 *pout_len = 0;
112                 return NULL;
113         }
114
115         ibl = strlen(str);
116         obl = 2 * ibl;
117         pin = str;
118         pout0 = xmalloc(obl + 2);
119         pout = pout0;
120
121 #ifdef HAVE_ICONV
122         if (iconv(uniconv->out_iconv_h, (ICONV_CONST char **) &pin, &ibl, &pout, &obl) == (size_t) - 1)
123         {
124                 printf("freerdp_uniconv_out: iconv() error\n");
125                 return NULL;
126         }
127 #else
128         while ((ibl > 0) && (obl > 0))
129         {
130                 unsigned int wc;
131
132                 wc = (unsigned int)(unsigned char)(*pin++);
133                 ibl--;
134                 if (wc >= 0xF0)
135                 {
136                         wc = (wc - 0xF0) << 18;
137                         wc += ((unsigned int)(unsigned char)(*pin++) - 0x80) << 12;
138                         wc += ((unsigned int)(unsigned char)(*pin++) - 0x80) << 6;
139                         wc += ((unsigned int)(unsigned char)(*pin++) - 0x80);
140                         ibl -= 3;
141                 }
142                 else if (wc >= 0xE0)
143                 {
144                         wc = (wc - 0xE0) << 12;
145                         wc += ((unsigned int)(unsigned char)(*pin++) - 0x80) << 6;
146                         wc += ((unsigned int)(unsigned char)(*pin++) - 0x80);
147                         ibl -= 2;
148                 }
149                 else if (wc >= 0xC0)
150                 {
151                         wc = (wc - 0xC0) << 6;
152                         wc += ((unsigned int)(unsigned char)(*pin++) - 0x80);
153                         ibl -= 1;
154                 }
155
156                 if (wc <= 0xFFFF)
157                 {
158                         *pout++ = (char)(wc & 0xFF);
159                         *pout++ = (char)(wc >> 8);
160                         obl -= 2;
161                 }
162                 else
163                 {
164                         wc -= 0x10000;
165                         *pout++ = (char)((wc >> 10) & 0xFF);
166                         *pout++ = (char)((wc >> 18) + 0xD8);
167                         *pout++ = (char)(wc & 0xFF);
168                         *pout++ = (char)(((wc >> 8) & 0x03) + 0xDC);
169                         obl -= 4;
170                 }
171         }
172 #endif
173
174         if (ibl > 0)
175         {
176                 printf("freerdp_uniconv_out: string not fully converted - %d chars left\n", (int) ibl);
177         }
178
179         *pout_len = pout - pout0;
180         *pout++ = 0; /* Add extra double zero termination */
181         *pout = 0;
182
183         return pout0;
184 }
185
186 /* Uppercase a unicode string */
187
188 void freerdp_uniconv_uppercase(UNICONV *uniconv, char *wstr, int length)
189 {
190         int i;
191         unsigned char* p;
192         unsigned int wc, uwc;
193
194         p = (unsigned char*)wstr;
195         for (i = 0; i < length; i++)
196         {
197                 wc = (unsigned int)(*p);
198                 wc += (unsigned int)(*(p + 1)) << 8;
199                 uwc = towupper(wc);
200                 if (uwc != wc)
201                 {
202                         *p = uwc & 0xFF;
203                         *(p + 1) = (uwc >> 8) & 0xFF;
204                 }
205                 p += 2;
206         }
207 }
208
209 UNICONV* freerdp_uniconv_new()
210 {
211         UNICONV *uniconv = xnew(UNICONV);
212
213 #ifdef HAVE_ICONV
214         uniconv->iconv = 1;
215         uniconv->in_iconv_h = iconv_open(DEFAULT_CODEPAGE, WINDOWS_CODEPAGE);
216         if (errno == EINVAL)
217         {
218                 printf("Error opening iconv converter to %s from %s\n", DEFAULT_CODEPAGE, WINDOWS_CODEPAGE);
219         }
220         uniconv->out_iconv_h = iconv_open(WINDOWS_CODEPAGE, DEFAULT_CODEPAGE);
221         if (errno == EINVAL)
222         {
223                 printf("Error opening iconv converter to %s from %s\n", WINDOWS_CODEPAGE, DEFAULT_CODEPAGE);
224         }
225 #endif
226
227         return uniconv;
228 }
229
230 void freerdp_uniconv_free(UNICONV *uniconv)
231 {
232         if (uniconv != NULL)
233         {
234 #ifdef HAVE_ICONV
235                 iconv_close(uniconv->in_iconv_h);
236                 iconv_close(uniconv->out_iconv_h);
237 #endif
238                 xfree(uniconv);
239         }
240 }