1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Dmitriy Balashov
5 * charconv.c
6 */
7
8 #include "builddefs.h"
9 #include "charconv.h"
10 #include "tunables.h"
11 #include "session.h"
12 #include "str.h"
13 #include "sysutil.h"
14
15 //#include "char_maps/utf8.map"
16 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
17 #include "char_maps/cyrillic.map"
18 #endif
19 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
20 #include "char_maps/western.map"
21 #endif
22 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
23 #include "char_maps/central.map"
24 #endif
25 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
26 #include "char_maps/soutern.map"
27 #endif
28 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
29 #include "char_maps/nordic.map"
30 #endif
31
32 /* Tables mapping supported codepage names to runtime variables */
33 static struct available_charsets
34 {
35 const char* p_charset_name;
36 int p_variable;
37 }
38 available_charsets_array[] =
39 {
40 { VSFTP_CP_NONE , VSFTP_C_NONE },
41 { VSFTP_CP_UTF_8 , VSFTP_C_UTF8 },
42 { VSFTP_CP_UTF8 , VSFTP_C_UTF8 },
43 // Cyrillic
44 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
45 { VSFTP_CP_WIN_1251 , VSFTP_C_1251 },
46 { VSFTP_CP_WIN1251 , VSFTP_C_1251 },
47 { VSFTP_CP_CP1251 , VSFTP_C_1251 },
48 { VSFTP_CP_1251 , VSFTP_C_1251 },
49 { VSFTP_CP_KOI8_R , VSFTP_C_878R },
50 { VSFTP_CP_KOI8R , VSFTP_C_878R },
51 { VSFTP_CP_CP878 , VSFTP_C_878R },
52 { VSFTP_CP_878 , VSFTP_C_878R },
53 { VSFTP_CP_CP878R , VSFTP_C_878R },
54 { VSFTP_CP_878R , VSFTP_C_878R },
55 { VSFTP_CP_KOI8_U , VSFTP_C_878U },
56 { VSFTP_CP_KOI8U , VSFTP_C_878U },
57 { VSFTP_CP_CP878U , VSFTP_C_878U },
58 { VSFTP_CP_878U , VSFTP_C_878U },
59 { VSFTP_CP_IBM866 , VSFTP_C_866 },
60 { VSFTP_CP_CP866 , VSFTP_C_866 },
61 { VSFTP_CP_866 , VSFTP_C_866 },
62 { VSFTP_CP_ISO8859_5 , VSFTP_C_ISO5 },
63 { VSFTP_CP_ISO5 , VSFTP_C_ISO5 },
64 #endif
65 // Western European
66 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
67 { VSFTP_CP_ISO8859_1 , VSFTP_C_ISO1 },
68 { VSFTP_CP_ISO1 , VSFTP_C_ISO1 },
69 { VSFTP_CP_LATIN1 , VSFTP_C_ISO1 },
70 { VSFTP_CP_ISO8859_15, VSFTP_C_ISO15 },
71 { VSFTP_CP_ISO15 , VSFTP_C_ISO15 },
72 { VSFTP_CP_LATIN9 , VSFTP_C_ISO15 },
73 { VSFTP_CP_WIN_1252 , VSFTP_C_1252 },
74 { VSFTP_CP_WIN1252 , VSFTP_C_1252 },
75 { VSFTP_CP_CP1252 , VSFTP_C_1252 },
76 { VSFTP_CP_1252 , VSFTP_C_1252 },
77 #endif
78 // Central European
79 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
80 { VSFTP_CP_ISO8859_2 , VSFTP_C_ISO2 },
81 { VSFTP_CP_ISO2 , VSFTP_C_ISO2 },
82 { VSFTP_CP_LATIN2 , VSFTP_C_ISO2 },
83 { VSFTP_CP_ISO8859_16, VSFTP_C_ISO16 },
84 { VSFTP_CP_ISO16 , VSFTP_C_ISO16 },
85 { VSFTP_CP_WIN_1250 , VSFTP_C_1250 },
86 { VSFTP_CP_WIN1250 , VSFTP_C_1250 },
87 { VSFTP_CP_CP1250 , VSFTP_C_1250 },
88 { VSFTP_CP_1250 , VSFTP_C_1250 },
89 #endif
90 // Soutern European
91 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
92 { VSFTP_CP_ISO8859_3 , VSFTP_C_ISO3 },
93 { VSFTP_CP_ISO3 , VSFTP_C_ISO3 },
94 { VSFTP_CP_LATIN3 , VSFTP_C_ISO3 },
95 #endif
96 // Nordic
97 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
98 { VSFTP_CP_ISO8859_4 , VSFTP_C_ISO4 },
99 { VSFTP_CP_ISO4 , VSFTP_C_ISO4 },
100 { VSFTP_CP_LATIN4 , VSFTP_C_ISO4 },
101 { VSFTP_CP_ISO8859_10, VSFTP_C_ISO10 },
102 { VSFTP_CP_ISO10 , VSFTP_C_ISO10 },
103 { VSFTP_CP_LATIN6 , VSFTP_C_ISO10 },
104 #endif
105 { 0, 0 }
106 };
107
108 /* Available convertions */
109 static struct available_convertions
110 {
111 int local;
112 int remote;
113 int localCharset;
114 int remoteCharset;
115 }
116 available_convertions_array[] =
117 { // Cyrillic
118 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
119 { VSFTP_C_UTF8 , VSFTP_C_1251 , VSFTP_CS_UTF8CYR , VSFTP_CS_1251 },
120 { VSFTP_C_UTF8 , VSFTP_C_878R , VSFTP_CS_UTF8CYR , VSFTP_CS_878R },
121 { VSFTP_C_UTF8 , VSFTP_C_878U , VSFTP_CS_UTF8CYR , VSFTP_CS_878U },
122 { VSFTP_C_UTF8 , VSFTP_C_866 , VSFTP_CS_UTF8CYR , VSFTP_CS_866 },
123 { VSFTP_C_UTF8 , VSFTP_C_ISO5 , VSFTP_CS_UTF8CYR , VSFTP_CS_ISO5 },
124 { VSFTP_C_1251 , VSFTP_C_UTF8 , VSFTP_CS_1251 , VSFTP_CS_UTF8CYR },
125 { VSFTP_C_1251 , VSFTP_C_878R , VSFTP_CS_1251 , VSFTP_CS_878R },
126 { VSFTP_C_1251 , VSFTP_C_878U , VSFTP_CS_1251 , VSFTP_CS_878U },
127 { VSFTP_C_1251 , VSFTP_C_866 , VSFTP_CS_1251 , VSFTP_CS_866 },
128 { VSFTP_C_1251 , VSFTP_C_ISO5 , VSFTP_CS_1251 , VSFTP_CS_ISO5 },
129 { VSFTP_C_878R , VSFTP_C_UTF8 , VSFTP_CS_878R , VSFTP_CS_UTF8CYR },
130 { VSFTP_C_878R , VSFTP_C_1251 , VSFTP_CS_878R , VSFTP_CS_1251 },
131 { VSFTP_C_878R , VSFTP_C_878U , VSFTP_CS_878R , VSFTP_CS_878U },
132 { VSFTP_C_878R , VSFTP_C_866 , VSFTP_CS_878R , VSFTP_CS_866 },
133 { VSFTP_C_878R , VSFTP_C_ISO5 , VSFTP_CS_878R , VSFTP_CS_ISO5 },
134 { VSFTP_C_866 , VSFTP_C_UTF8 , VSFTP_CS_866 , VSFTP_CS_UTF8CYR },
135 { VSFTP_C_866 , VSFTP_C_1251 , VSFTP_CS_866 , VSFTP_CS_1251 },
136 { VSFTP_C_866 , VSFTP_C_878R , VSFTP_CS_866 , VSFTP_CS_878R },
137 { VSFTP_C_866 , VSFTP_C_878U , VSFTP_CS_866 , VSFTP_CS_878U },
138 { VSFTP_C_866 , VSFTP_C_ISO5 , VSFTP_CS_866 , VSFTP_CS_ISO5 },
139 { VSFTP_C_ISO5 , VSFTP_C_UTF8 , VSFTP_CS_ISO5 , VSFTP_CS_UTF8CYR },
140 { VSFTP_C_ISO5 , VSFTP_C_1251 , VSFTP_CS_ISO5 , VSFTP_CS_1251 },
141 { VSFTP_C_ISO5 , VSFTP_C_878R , VSFTP_CS_ISO5 , VSFTP_CS_878R },
142 { VSFTP_C_ISO5 , VSFTP_C_878U , VSFTP_CS_ISO5 , VSFTP_CS_878U },
143 { VSFTP_C_ISO5 , VSFTP_C_866 , VSFTP_CS_ISO5 , VSFTP_CS_866 },
144 #endif
145 // Western European
146 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
147 { VSFTP_C_UTF8 , VSFTP_C_ISO1 , VSFTP_CS_UTF8WEST , VSFTP_CS_ISO1 },
148 { VSFTP_C_UTF8 , VSFTP_C_ISO15 , VSFTP_CS_UTF8WEST , VSFTP_CS_ISO15 },
149 { VSFTP_C_UTF8 , VSFTP_C_1252 , VSFTP_CS_UTF8WEST , VSFTP_CS_1252 },
150 { VSFTP_C_ISO1 , VSFTP_C_UTF8 , VSFTP_CS_ISO1 , VSFTP_CS_UTF8WEST },
151 { VSFTP_C_ISO1 , VSFTP_C_ISO15 , VSFTP_CS_ISO1 , VSFTP_CS_ISO15 },
152 { VSFTP_C_ISO1 , VSFTP_C_1252 , VSFTP_CS_ISO1 , VSFTP_CS_1252 },
153 { VSFTP_C_ISO15 , VSFTP_C_UTF8 , VSFTP_CS_ISO15 , VSFTP_CS_UTF8WEST },
154 { VSFTP_C_ISO15 , VSFTP_C_ISO1 , VSFTP_CS_ISO15 , VSFTP_CS_ISO1 },
155 { VSFTP_C_ISO15 , VSFTP_C_1252 , VSFTP_CS_ISO15 , VSFTP_CS_1252 },
156 { VSFTP_C_1252 , VSFTP_C_UTF8 , VSFTP_CS_1252 , VSFTP_CS_UTF8WEST },
157 { VSFTP_C_1252 , VSFTP_C_ISO1 , VSFTP_CS_1252 , VSFTP_CS_ISO1 },
158 { VSFTP_C_1252 , VSFTP_C_ISO15 , VSFTP_CS_1252 , VSFTP_CS_ISO15 },
159 #endif
160 // Central European
161 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
162 { VSFTP_C_UTF8 , VSFTP_C_ISO2 , VSFTP_CS_UTF8CENT , VSFTP_CS_ISO2 },
163 { VSFTP_C_UTF8 , VSFTP_C_ISO16 , VSFTP_CS_UTF8CENT , VSFTP_CS_ISO16 },
164 { VSFTP_C_UTF8 , VSFTP_C_1250 , VSFTP_CS_UTF8CENT , VSFTP_CS_1250 },
165 { VSFTP_C_ISO2 , VSFTP_C_UTF8 , VSFTP_CS_ISO2 , VSFTP_CS_UTF8CENT },
166 { VSFTP_C_ISO2 , VSFTP_C_ISO16 , VSFTP_CS_ISO2 , VSFTP_CS_ISO16 },
167 { VSFTP_C_ISO2 , VSFTP_C_1250 , VSFTP_CS_ISO2 , VSFTP_CS_1250 },
168 { VSFTP_C_ISO16 , VSFTP_C_UTF8 , VSFTP_CS_ISO16 , VSFTP_CS_UTF8CENT },
169 { VSFTP_C_ISO16 , VSFTP_C_ISO2 , VSFTP_CS_ISO16 , VSFTP_CS_ISO2 },
170 { VSFTP_C_ISO16 , VSFTP_C_1250 , VSFTP_CS_ISO16 , VSFTP_CS_1250 },
171 { VSFTP_C_1250 , VSFTP_C_UTF8 , VSFTP_CS_1250 , VSFTP_CS_UTF8CENT },
172 { VSFTP_C_1250 , VSFTP_C_ISO2 , VSFTP_CS_1250 , VSFTP_CS_ISO2 },
173 { VSFTP_C_1250 , VSFTP_C_ISO16 , VSFTP_CS_1250 , VSFTP_CS_ISO16 },
174 #endif
175 // Soutern European
176 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
177 { VSFTP_C_UTF8 , VSFTP_C_ISO3 , VSFTP_CS_UTF8SOUT , VSFTP_CS_ISO3 },
178 { VSFTP_C_ISO3 , VSFTP_C_UTF8 , VSFTP_CS_ISO3 , VSFTP_CS_UTF8SOUT },
179 #endif
180 // Nordic
181 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
182 { VSFTP_C_UTF8 , VSFTP_C_ISO4 , VSFTP_CS_UTF8NORD , VSFTP_CS_ISO4 },
183 { VSFTP_C_UTF8 , VSFTP_C_ISO10 , VSFTP_CS_UTF8NORD , VSFTP_CS_ISO10 },
184 { VSFTP_C_ISO4 , VSFTP_C_ISO10 , VSFTP_CS_ISO4 , VSFTP_CS_ISO10 },
185 { VSFTP_C_ISO4 , VSFTP_C_UTF8 , VSFTP_CS_ISO4 , VSFTP_CS_UTF8NORD },
186 { VSFTP_C_ISO10 , VSFTP_C_ISO4 , VSFTP_CS_ISO10 , VSFTP_CS_ISO4 },
187 { VSFTP_C_ISO10 , VSFTP_C_UTF8 , VSFTP_CS_ISO10 , VSFTP_CS_UTF8NORD },
188 #endif
189 { 0, 0, 0, 0 }
190 };
191
192 map_ptr map_array[] =
193 {
194 0,
195 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
196 codepage_utf8cyr_array, codepage_win1251_array, codepage_koi8r_array,
197 codepage_ibm866_array, codepage_iso5_array, codepage_koi8u_array,
198 #else
199 0, 0, 0, 0, 0, 0,
200 #endif
201
202 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
203 codepage_utf8west_array, codepage_iso1_array, codepage_iso15_array,
204 codepage_win1252_array,
205 #else
206 0, 0, 0, 0,
207 #endif
208
209 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
210 codepage_utf8cent_array, codepage_iso2_array, codepage_iso16_array,
211 codepage_win1250_array,
212 #else
213 0, 0, 0, 0,
214 #endif
215
216 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
217 codepage_utf8sout_array, codepage_iso3_array,
218 #else
219 0, 0,
220 #endif
221
222 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
223 codepage_utf8nord_array, codepage_iso4_array, codepage_iso10_array,
224 #else
225 0, 0, 0,
226 #endif
227
228 0
229 };
230
231 /* Initial table for work with unprintable chars */
232 /*
233 map_ptr init_array[] =
234 {
235 0, 0,
236 //codepage_utf8_array,
237
238 #ifdef VSFTP_CHARCONV_SUPPORT_CYRILLIC
239 codepage_win1251_array, codepage_koi8r_array, codepage_ibm866_array,
240 codepage_iso5_array, codepage_koi8u_array,
241 #else
242 0, 0, 0, 0, 0,
243 #endif
244
245 #ifdef VSFTP_CHARCONV_SUPPORT_WESTERN
246 codepage_iso1_array, codepage_iso15_array, codepage_win1252_array,
247 #else
248 0, 0, 0,
249 #endif
250
251 #ifdef VSFTP_CHARCONV_SUPPORT_CENTRAL
252 codepage_iso2_array, codepage_iso16_array, codepage_win1250_array,
253 #else
254 0, 0, 0,
255 #endif
256
257 #ifdef VSFTP_CHARCONV_SUPPORT_SOUTERN
258 codepage_iso3_array,
259 #else
260 0,
261 #endif
262
263 #ifdef VSFTP_CHARCONV_SUPPORT_NORDIC
264 codepage_iso4_array, codepage_iso10_array,
265 #else
266 0, 0,
267 #endif
268
269 0
270 };
271 */
272
273 map_ptr localMap = 0, remoteMap = 0;
274 map_ptr localTbl = 0, remoteTbl = 0;
275
276 void char_convertion(struct mystr* p_str, int direction, char unprintable);
277 void init_tables(map_ptr* map, map_ptr* table, int indexx);
278 static int char_len_utf8(unsigned int s);
279 static unsigned int bsearch_index(map_ptr map, unsigned int low, unsigned int high, unsigned int char_code);
280
vsf_charconv_charset_name(int code)281 const char* vsf_charconv_charset_name(int code)
282 {
283 int i = 0;
284 while (available_charsets_array[i].p_charset_name && available_charsets_array[i].p_variable != code) i++;
285 return available_charsets_array[i].p_charset_name;
286 }
287
vsf_charconv_codepage(const char * p_str)288 int vsf_charconv_codepage(const char* p_str)
289 {
290 const struct available_charsets* charsets = available_charsets_array;
291
292 while (charsets->p_charset_name != 0)
293 {
294 if (str_equal_str(charsets->p_charset_name, p_str))
295 {
296 return charsets->p_variable;
297 }
298 charsets++;
299 }
300
301 return 0;
302 }
303
vsf_charconv_avail_convertion(int localCode,int remoteCode)304 int vsf_charconv_avail_convertion(int localCode, int remoteCode)
305 {
306 const struct available_convertions* aconv = available_convertions_array;
307
308 while (aconv->local != 0)
309 {
310 if (localCode == aconv->local && remoteCode == aconv->remote)
311 {
312 init_tables(&localMap, &localTbl, aconv->localCharset);
313 init_tables(&remoteMap, &remoteTbl, aconv->remoteCharset);
314 return 1;
315 }
316 aconv++;
317 }
318
319 return 0;
320 }
321
vsf_charconv_init_local_codepage(int localCode)322 void vsf_charconv_init_local_codepage(int localCode)
323 {
324 if (!localCode || localCode == VSFTP_C_UTF8)
325 {
326 return;
327 }
328
329 init_tables(&localMap, &localTbl, localCode);
330
331 /*
332 localTbl = init_array[localCode];
333 localMap = vsf_sysutil_malloc((localTbl[0].order + 1) * sizeof(_codepage_map));
334 localMap[0].char_code = localTbl[0].char_code;
335 localMap[0].order = localTbl[0].order;
336
337 int indexx = 1;
338 while (localTbl[indexx].char_code)
339 {
340 if (localTbl[indexx].order)
341 {
342 localMap[localTbl[indexx].order].char_code = localTbl[indexx].char_code;
343 localMap[localTbl[indexx].order].order = indexx;
344 }
345 indexx++;
346 }
347 */
348
349 return;
350 }
351
vsf_charconv_convert(struct vsf_session * p_sess,struct mystr * p_str,int direction)352 void vsf_charconv_convert(struct vsf_session* p_sess, struct mystr* p_str, int direction)
353 {
354 if (!p_sess->enable_convertion || !p_sess->remote_charset || !tunable_local_codepage) return;
355
356 char_convertion(p_str, direction, '?');
357 }
358
vsf_charconv_replace_unprintable(struct mystr * p_str,char new_char)359 void vsf_charconv_replace_unprintable(struct mystr* p_str, char new_char)
360 {
361 if (localMap) char_convertion(p_str, VSFTP_CONVDIRECT_UNPRINTABLE, new_char);
362 }
363
init_tables(map_ptr * map,map_ptr * table,int indexx)364 void init_tables(map_ptr* map, map_ptr* table, int indexx)
365 {
366 *table = map_array[indexx];
367
368 if (*map) vsf_sysutil_free(*map);
369
370 *map = vsf_sysutil_malloc(((*table)[0].order + 1) * sizeof(_codepage_map));
371 (*map)[0].char_code = (*table)[0].char_code;
372 (*map)[0].order = (*table)[0].order;
373
374 indexx = 1;
375 while ((*table)[indexx].char_code)
376 {
377 if ((*table)[indexx].order)
378 {
379 (*map)[(*table)[indexx].order].char_code = (*table)[indexx].char_code;
380 (*map)[(*table)[indexx].order].order = indexx;
381 }
382 indexx++;
383 }
384
385 return;
386 }
387
char_len_utf8(unsigned int s)388 static int char_len_utf8(unsigned int s)
389 {
390 int len = 1;
391 if ((s & 0x80) == 0x00) len = 1;
392 else if ((s & 0xe0) == 0xc0) len = 2;
393 else if ((s & 0xf0) == 0xe0) len = 3;
394 else if ((s & 0xf8) == 0xf0) len = 4;
395 // else if ((s & 0xfc) == 0xf8) len = 5;
396 // else if ((s & 0xfe) == 0xce) len = 6;
397 return (len);
398 }
399
bsearch_index(map_ptr map,unsigned int low,unsigned int high,unsigned int char_code)400 static unsigned int bsearch_index(map_ptr map, unsigned int low, unsigned int high, unsigned int char_code)
401 {
402 unsigned int m, l = low, r = high;
403
404 m = (l + r) >> 1;
405 while ((m != 0) && (map[m].char_code != char_code))
406 {
407 if (map[m].char_code < char_code) l = m + 1;
408 else
409 if (map[m].char_code > char_code) r = m - 1;
410 if (l > r)
411 return 0;
412 else
413 m = (l + r) >> 1;
414 }
415
416 if (m) m = map[m].order;
417 return m;
418 }
419
char_convertion(struct mystr * p_str,int direction,char unprintable)420 void char_convertion(struct mystr* p_str, int direction, char unprintable)
421 {
422 const char* srcbuf;
423 unsigned int srclen;
424 char* dstbuf;
425 map_ptr src, dst;
426 unsigned int sl;
427 unsigned int srcpos = 0, dstpos = 0;
428 unsigned int char_code = 0;
429
430 srclen = str_getlen(p_str); // Len of source string
431 srcbuf = str_getbuf(p_str);
432
433 if (direction == VSFTP_CONVDIRECT_FORWARD)
434 {
435 src = localMap;
436 dst = remoteTbl;
437 }
438 else
439 if (direction == VSFTP_CONVDIRECT_BACKWARD)
440 {
441 src = remoteMap;
442 dst = localTbl;
443 }
444 else
445 {
446 src = localMap;
447 dst = localTbl;
448 }
449
450 if (!src || !dst)
451 {
452 return;
453 }
454
455 dstbuf = vsf_sysutil_malloc(srclen * dst[0].char_code + dst[0].char_code);
456
457 while (srcpos < srclen)
458 {
459 char_code = (unsigned char)srcbuf [srcpos++];
460 if (src[0].char_code > 1)
461 {
462 sl = char_len_utf8(char_code);
463 while (sl-- > 1)
464 {
465 char_code = (char_code << 8) | (unsigned char)srcbuf [srcpos++];
466 }
467 }
468
469 if (char_code > 127)
470 {
471 sl = bsearch_index(src, 1, src[0].order, char_code);
472 char_code = 0;
473 if (sl) char_code = dst[sl].char_code;
474 }
475
476 if (char_code == 13 || char_code == 10)
477 {
478 char_code = 0;
479 }
480 else
481 if (char_code < 32 && char_code != 9)
482 {
483 char_code = (unsigned int)unprintable;
484 }
485
486 if (char_code > 0 || direction != VSFTP_CONVDIRECT_UNPRINTABLE)
487 {
488 if (char_code & 0xff000000) dstbuf [dstpos++] = (char)((char_code >> 24) & 0xff);
489 if (char_code & 0x00ff0000) dstbuf [dstpos++] = (char)((char_code >> 16) & 0xff);
490 if (char_code & 0x0000ff00) dstbuf [dstpos++] = (char)((char_code >> 8) & 0xff);
491 if (char_code & 0x000000ff) dstbuf [dstpos++] = (char)((char_code ) & 0xff);
492 }
493 }
494
495 dstbuf[dstpos] = '\0';
496
497 str_alloc_text(p_str, dstbuf);
498 vsf_sysutil_free(dstbuf);
499 }
500